diff --git a/src/ChangeLog b/src/ChangeLog index 706751bbdfa..200c20fc3bf 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,30 @@ +2011-04-15 Paul Eggert + + emacs_write: Accept and return EMACS_INT for sizes. + See http://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00514.html + et seq. + * gnutls.c, gnutls.h (emacs_gnutls_read, emacs_gnutls_write): + Accept and return EMACS_INT. + (emacs_gnutls_write): Return the number of bytes written on + partial writes. + * sysdep.c, lisp.h (emacs_read, emacs_write): Likewise. + (emacs_read, emacs_write): Remove check for negative size, as the + Emacs source code has been audited now. + * sysdep.c (MAX_RW_COUNT): New macro, to work around kernel bugs. + (emacs_read, emacs_write): Use it. + * process.c (send_process): Adjust to the new signatures of + emacs_write and emacs_gnutls_write. Do not attempt to store + a byte offset into an 'int'; it might overflow. + See http://lists.gnu.org/archive/html/emacs-devel/2011-04/msg00483.html + + * sound.c: Don't assume sizes fit in 'int'. + (struct sound_device.period_size, alsa_period_size): + Return EMACS_INT, not int. + (struct sound_device.write, vox_write, alsa_write): + Accept EMACS_INT, not int. + (wav_play, au_play): Use EMACS_INT to store sizes and to + record read return values. + 2011-04-15 Ben Key * keyboard.c (Qundefined): Don't declare static since it is diff --git a/src/gnutls.c b/src/gnutls.c index d9e4dcec15a..d7328e114c7 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -70,12 +70,12 @@ emacs_gnutls_handshake (struct Lisp_Process *proc) } } -ssize_t +EMACS_INT emacs_gnutls_write (int fildes, struct Lisp_Process *proc, const char *buf, - size_t nbyte) + EMACS_INT nbyte) { ssize_t rtnval; - size_t bytes_written; + EMACS_INT bytes_written; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) { @@ -85,7 +85,7 @@ emacs_gnutls_write (int fildes, struct Lisp_Process *proc, const char *buf, #ifdef EAGAIN errno = EAGAIN; #endif - return -1; + return 0; } bytes_written = 0; @@ -99,7 +99,7 @@ emacs_gnutls_write (int fildes, struct Lisp_Process *proc, const char *buf, if (rtnval == GNUTLS_E_AGAIN || rtnval == GNUTLS_E_INTERRUPTED) continue; else - return (bytes_written ? bytes_written : -1); + break; } buf += rtnval; @@ -110,9 +110,9 @@ emacs_gnutls_write (int fildes, struct Lisp_Process *proc, const char *buf, return (bytes_written); } -ssize_t +EMACS_INT emacs_gnutls_read (int fildes, struct Lisp_Process *proc, char *buf, - size_t nbyte) + EMACS_INT nbyte) { ssize_t rtnval; gnutls_session_t state = proc->gnutls_state; diff --git a/src/gnutls.h b/src/gnutls.h index b39131b6236..5240d94c2ad 100644 --- a/src/gnutls.h +++ b/src/gnutls.h @@ -50,12 +50,12 @@ typedef enum #define GNUTLS_LOG2(level, max, string, extra) if (level <= max) { gnutls_log_function2 (level, "(Emacs) " string, extra); } -ssize_t +EMACS_INT emacs_gnutls_write (int fildes, struct Lisp_Process *proc, const char *buf, - size_t nbyte); -ssize_t + EMACS_INT nbyte); +EMACS_INT emacs_gnutls_read (int fildes, struct Lisp_Process *proc, char *buf, - size_t nbyte); + EMACS_INT nbyte); extern void syms_of_gnutls (void); diff --git a/src/lisp.h b/src/lisp.h index 96c8e42995b..581835dd32b 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3317,8 +3317,8 @@ extern long get_random (void); extern void seed_random (long); extern int emacs_open (const char *, int, int); extern int emacs_close (int); -extern ssize_t emacs_read (int, char *, size_t); -extern ssize_t emacs_write (int, const char *, size_t); +extern EMACS_INT emacs_read (int, char *, EMACS_INT); +extern EMACS_INT emacs_write (int, const char *, EMACS_INT); enum { READLINK_BUFSIZE = 1024 }; extern char *emacs_readlink (const char *, char [READLINK_BUFSIZE]); #ifndef HAVE_MEMSET diff --git a/src/process.c b/src/process.c index e9ac324845b..c9c6ab6d4b3 100644 --- a/src/process.c +++ b/src/process.c @@ -5368,6 +5368,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf, /* Send this batch, using one or more write calls. */ while (this > 0) { + EMACS_INT written = 0; int outfd = p->outfd; old_sigpipe = (void (*) (int)) signal (SIGPIPE, send_process_trap); #ifdef DATAGRAM_SOCKETS @@ -5376,7 +5377,9 @@ send_process (volatile Lisp_Object proc, const char *volatile buf, rv = sendto (outfd, buf, this, 0, datagram_address[outfd].sa, datagram_address[outfd].len); - if (rv < 0 && errno == EMSGSIZE) + if (0 <= rv) + written = rv; + else if (errno == EMSGSIZE) { signal (SIGPIPE, old_sigpipe); report_file_error ("sending datagram", @@ -5388,12 +5391,13 @@ send_process (volatile Lisp_Object proc, const char *volatile buf, { #ifdef HAVE_GNUTLS if (XPROCESS (proc)->gnutls_p) - rv = emacs_gnutls_write (outfd, - XPROCESS (proc), - buf, this); + written = emacs_gnutls_write (outfd, + XPROCESS (proc), + buf, this); else #endif - rv = emacs_write (outfd, buf, this); + written = emacs_write (outfd, buf, this); + rv = (written ? 0 : -1); #ifdef ADAPTIVE_READ_BUFFERING if (p->read_output_delay > 0 && p->adaptive_read_buffering == 1) @@ -5420,7 +5424,7 @@ send_process (volatile Lisp_Object proc, const char *volatile buf, that may allow the program to finish doing output and read more. */ { - int offset = 0; + EMACS_INT offset = 0; #ifdef BROKEN_PTY_READ_AFTER_EAGAIN /* A gross hack to work around a bug in FreeBSD. @@ -5466,16 +5470,14 @@ send_process (volatile Lisp_Object proc, const char *volatile buf, offset); else if (STRINGP (object)) buf = offset + SSDATA (object); - - rv = 0; } else /* This is a real error. */ report_file_error ("writing to process", Fcons (proc, Qnil)); } - buf += rv; - len -= rv; - this -= rv; + buf += written; + len -= written; + this -= written; } } } diff --git a/src/sound.c b/src/sound.c index 697e81c814b..794c8e64e54 100644 --- a/src/sound.c +++ b/src/sound.c @@ -235,11 +235,11 @@ struct sound_device /* Return a preferred data size in bytes to be sent to write (below) each time. 2048 is used if this is NULL. */ - int (* period_size) (struct sound_device *sd); + EMACS_INT (* period_size) (struct sound_device *sd); /* Write NYBTES bytes from BUFFER to device SD. */ void (* write) (struct sound_device *sd, const char *buffer, - int nbytes); + EMACS_INT nbytes); /* A place for devices to store additional data. */ void *data; @@ -291,7 +291,7 @@ static void vox_configure (struct sound_device *); static void vox_close (struct sound_device *sd); static void vox_choose_format (struct sound_device *, struct sound *); static int vox_init (struct sound_device *); -static void vox_write (struct sound_device *, const char *, int); +static void vox_write (struct sound_device *, const char *, EMACS_INT); static void find_sound_type (struct sound *); static u_int32_t le2hl (u_int32_t); static u_int16_t le2hs (u_int16_t); @@ -600,9 +600,9 @@ wav_play (struct sound *s, struct sound_device *sd) else { char *buffer; - int nbytes = 0; - int blksize = sd->period_size ? sd->period_size (sd) : 2048; - int data_left = header->data_length; + EMACS_INT nbytes = 0; + EMACS_INT blksize = sd->period_size ? sd->period_size (sd) : 2048; + EMACS_INT data_left = header->data_length; buffer = (char *) alloca (blksize); lseek (s->fd, sizeof *header, SEEK_SET); @@ -690,9 +690,9 @@ au_play (struct sound *s, struct sound_device *sd) SBYTES (s->data) - header->data_offset); else { - int blksize = sd->period_size ? sd->period_size (sd) : 2048; + EMACS_INT blksize = sd->period_size ? sd->period_size (sd) : 2048; char *buffer; - int nbytes; + EMACS_INT nbytes; /* Seek */ lseek (s->fd, header->data_offset, SEEK_SET); @@ -895,10 +895,9 @@ vox_init (struct sound_device *sd) /* Write NBYTES bytes from BUFFER to device SD. */ static void -vox_write (struct sound_device *sd, const char *buffer, int nbytes) +vox_write (struct sound_device *sd, const char *buffer, EMACS_INT nbytes) { - ssize_t nwritten = emacs_write (sd->fd, buffer, nbytes); - if (nwritten < 0) + if (emacs_write (sd->fd, buffer, nbytes) != nbytes) sound_perror ("Error writing to sound device"); } @@ -953,7 +952,7 @@ alsa_open (struct sound_device *sd) alsa_sound_perror (file, err); } -static int +static EMACS_INT alsa_period_size (struct sound_device *sd) { struct alsa_params *p = (struct alsa_params *) sd->data; @@ -1156,13 +1155,13 @@ alsa_choose_format (struct sound_device *sd, struct sound *s) /* Write NBYTES bytes from BUFFER to device SD. */ static void -alsa_write (struct sound_device *sd, const char *buffer, int nbytes) +alsa_write (struct sound_device *sd, const char *buffer, EMACS_INT nbytes) { struct alsa_params *p = (struct alsa_params *) sd->data; /* The the third parameter to snd_pcm_writei is frames, not bytes. */ int fact = snd_pcm_format_size (sd->format, 1) * sd->channels; - int nwritten = 0; + EMACS_INT nwritten = 0; int err; while (nwritten < nbytes) diff --git a/src/sysdep.c b/src/sysdep.c index 0d9b31f35cd..6b6e3e9e791 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -1825,41 +1825,47 @@ emacs_close (int fd) return rtnval; } -ssize_t -emacs_read (int fildes, char *buf, size_t nbyte) +/* Maximum number of bytes to read or write in a single system call. + This works around a serious bug in Linux kernels before 2.6.16; see + . + It's likely to work around similar bugs in other operating systems, so do it + on all platforms. Round INT_MAX down to a page size, with the conservative + assumption that page sizes are at most 2**18 bytes (any kernel with a + page size larger than that shouldn't have the bug). */ +#ifndef MAX_RW_COUNT +#define MAX_RW_COUNT (INT_MAX >> 18 << 18) +#endif + +/* Read from FILEDESC to a buffer BUF with size NBYTE, retrying if interrupted. + Return the number of bytes read, which might be less than NBYTE. + On error, set errno and return -1. */ +EMACS_INT +emacs_read (int fildes, char *buf, EMACS_INT nbyte) { register ssize_t rtnval; - /* Defend against the possibility that a buggy caller passes a negative NBYTE - argument, which would be converted to a large unsigned size_t NBYTE. This - defense prevents callers from doing large writes, unfortunately. This - size restriction can be removed once we have carefully checked that there - are no such callers. */ - if ((ssize_t) nbyte < 0) - abort (); - - while ((rtnval = read (fildes, buf, nbyte)) == -1 + while ((rtnval = read (fildes, buf, min (nbyte, MAX_RW_COUNT))) == -1 && (errno == EINTR)) QUIT; return (rtnval); } -ssize_t -emacs_write (int fildes, const char *buf, size_t nbyte) +/* Write to FILEDES from a buffer BUF with size NBYTE, retrying if interrupted + or if a partial write occurs. Return the number of bytes written, setting + errno if this is less than NBYTE. */ +EMACS_INT +emacs_write (int fildes, const char *buf, EMACS_INT nbyte) { - register ssize_t rtnval, bytes_written; - - /* Defend against negative NBYTE, as in emacs_read. */ - if ((ssize_t) nbyte < 0) - abort (); + ssize_t rtnval; + EMACS_INT bytes_written; bytes_written = 0; - while (nbyte != 0) + while (nbyte > 0) { - rtnval = write (fildes, buf, nbyte); + rtnval = write (fildes, buf, min (nbyte, MAX_RW_COUNT)); - if (rtnval == -1) + if (rtnval < 0) { if (errno == EINTR) { @@ -1871,13 +1877,14 @@ emacs_write (int fildes, const char *buf, size_t nbyte) continue; } else - return (bytes_written ? bytes_written : -1); + break; } buf += rtnval; nbyte -= rtnval; bytes_written += rtnval; } + return (bytes_written); }