From 0ea7bb3578998ebdf7c4903e7f38a8abf2e41b68 Mon Sep 17 00:00:00 2001 From: Philipp Stephani <phst@google.com> Date: Wed, 30 Dec 2020 22:33:22 +0100 Subject: [PATCH] Consistently check for FD_SETSIZE overflow. Previously this was only checked in a few places. Now assert that file descriptors are within the expected range whenever we'd otherwise introduce undefined behavior. * src/process.c (add_read_fd, add_process_read_fd, delete_read_fd) (recompute_max_desc, delete_write_fd, compute_input_wait_mask) (compute_non_process_wait_mask, compute_non_keyboard_wait_mask) (compute_write_mask, clear_waiting_thread_info) (update_processes_for_thread_death, Fset_process_thread) (create_process, create_pty, Fmake_pipe_process) (Fprocess_datagram_address, Fset_process_datagram_address) (Fmake_serial_process, finish_after_tls_connection) (connect_network_socket, deactivate_process) (server_accept_connection, wait_reading_process_output) (read_process_output, read_and_dispose_of_process_output) (send_process, Fcontinue_process, Fprocess_send_eof) (Fprocess_filter_multibyte_p, keyboard_bit_set) (add_timer_wait_descriptor, setup_process_coding_systems): Add assertions to document and check that file descriptors are within the expected range when used as file descriptor set elements or array subscripts. --- src/process.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/process.c b/src/process.c index 28ab15c9038..7c56366d8e1 100644 --- a/src/process.c +++ b/src/process.c @@ -465,6 +465,7 @@ add_read_fd (int fd, fd_callback func, void *data) { add_keyboard_wait_descriptor (fd); + eassert (0 <= fd && fd < FD_SETSIZE); fd_callback_info[fd].func = func; fd_callback_info[fd].data = data; } @@ -485,6 +486,7 @@ static void add_process_read_fd (int fd) { add_non_keyboard_read_fd (fd); + eassert (0 <= fd && fd < FD_SETSIZE); fd_callback_info[fd].flags |= PROCESS_FD; } @@ -495,6 +497,7 @@ delete_read_fd (int fd) { delete_keyboard_wait_descriptor (fd); + eassert (0 <= fd && fd < FD_SETSIZE); if (fd_callback_info[fd].flags == 0) { fd_callback_info[fd].func = 0; @@ -534,6 +537,7 @@ recompute_max_desc (void) { int fd; + eassert (max_desc < FD_SETSIZE); for (fd = max_desc; fd >= 0; --fd) { if (fd_callback_info[fd].flags != 0) @@ -542,6 +546,7 @@ recompute_max_desc (void) break; } } + eassert (max_desc < FD_SETSIZE); } /* Stop monitoring file descriptor FD for when write is possible. */ @@ -549,6 +554,7 @@ recompute_max_desc (void) void delete_write_fd (int fd) { + eassert (0 <= fd && fd < FD_SETSIZE); if ((fd_callback_info[fd].flags & NON_BLOCKING_CONNECT_FD) != 0) { if (--num_pending_connects < 0) @@ -571,6 +577,7 @@ compute_input_wait_mask (fd_set *mask) int fd; FD_ZERO (mask); + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; ++fd) { if (fd_callback_info[fd].thread != NULL @@ -593,6 +600,7 @@ compute_non_process_wait_mask (fd_set *mask) int fd; FD_ZERO (mask); + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; ++fd) { if (fd_callback_info[fd].thread != NULL @@ -616,6 +624,7 @@ compute_non_keyboard_wait_mask (fd_set *mask) int fd; FD_ZERO (mask); + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; ++fd) { if (fd_callback_info[fd].thread != NULL @@ -639,6 +648,7 @@ compute_write_mask (fd_set *mask) int fd; FD_ZERO (mask); + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; ++fd) { if (fd_callback_info[fd].thread != NULL @@ -660,6 +670,7 @@ clear_waiting_thread_info (void) { int fd; + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; ++fd) { if (fd_callback_info[fd].waiting_thread == current_thread) @@ -936,8 +947,10 @@ update_processes_for_thread_death (Lisp_Object dying_thread) struct Lisp_Process *proc = XPROCESS (process); pset_thread (proc, Qnil); + eassert (proc->infd < FD_SETSIZE); if (proc->infd >= 0) fd_callback_info[proc->infd].thread = NULL; + eassert (proc->outfd < FD_SETSIZE); if (proc->outfd >= 0) fd_callback_info[proc->outfd].thread = NULL; } @@ -1378,8 +1391,10 @@ If THREAD is nil, the process is unlocked. */) proc = XPROCESS (process); pset_thread (proc, thread); + eassert (proc->infd < FD_SETSIZE); if (proc->infd >= 0) fd_callback_info[proc->infd].thread = tstate; + eassert (proc->outfd < FD_SETSIZE); if (proc->outfd >= 0) fd_callback_info[proc->outfd].thread = tstate; @@ -2108,6 +2123,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) fcntl (outchannel, F_SETFL, O_NONBLOCK); /* Record this as an active process, with its channels. */ + eassert (0 <= inchannel && inchannel < FD_SETSIZE); chan_process[inchannel] = process; p->infd = inchannel; p->outfd = outchannel; @@ -2215,6 +2231,7 @@ create_pty (Lisp_Object process) /* Record this as an active process, with its channels. As a result, child_setup will close Emacs's side of the pipes. */ + eassert (0 <= pty_fd && pty_fd < FD_SETSIZE); chan_process[pty_fd] = process; p->infd = pty_fd; p->outfd = pty_fd; @@ -2308,6 +2325,7 @@ usage: (make-pipe-process &rest ARGS) */) #endif /* Record this as an active process, with its channels. */ + eassert (0 <= inchannel && inchannel < FD_SETSIZE); chan_process[inchannel] = proc; p->infd = inchannel; p->outfd = outchannel; @@ -2637,6 +2655,7 @@ set up yet, this function will block until socket setup has completed. */) return Qnil; channel = XPROCESS (process)->infd; + eassert (0 <= channel && channel < FD_SETSIZE); return conv_sockaddr_to_lisp (datagram_address[channel].sa, datagram_address[channel].len); } @@ -2665,6 +2684,7 @@ set up yet, this function will block until socket setup has completed. */) channel = XPROCESS (process)->infd; len = get_lisp_to_sockaddr_size (address, &family); + eassert (0 <= channel && channel < FD_SETSIZE); if (len == 0 || datagram_address[channel].len != len) return Qnil; conv_lisp_to_sockaddr (family, address, datagram_address[channel].sa, len); @@ -3043,6 +3063,7 @@ usage: (make-serial-process &rest ARGS) */) p->outfd = fd; if (fd > max_desc) max_desc = fd; + eassert (0 <= fd && fd < FD_SETSIZE); chan_process[fd] = proc; buffer = Fplist_get (contact, QCbuffer); @@ -3213,6 +3234,7 @@ finish_after_tls_connection (Lisp_Object proc) Fplist_get (contact, QChost), Fplist_get (contact, QCservice)); + eassert (p->outfd < FD_SETSIZE); if (NILP (result)) { pset_status (p, list2 (Qfailed, @@ -3463,6 +3485,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, #ifdef DATAGRAM_SOCKETS if (p->socktype == SOCK_DGRAM) { + eassert (0 <= s && s < FD_SETSIZE); if (datagram_address[s].sa) emacs_abort (); @@ -3527,6 +3550,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, inch = s; outch = s; + eassert (0 <= inch && inch < FD_SETSIZE); chan_process[inch] = proc; fcntl (inch, F_SETFL, O_NONBLOCK); @@ -3553,6 +3577,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos, if (! (connecting_status (p->status) && EQ (XCDR (p->status), addrinfos))) pset_status (p, Fcons (Qconnect, addrinfos)); + eassert (0 <= inch && inch < FD_SETSIZE); if ((fd_callback_info[inch].flags & NON_BLOCKING_CONNECT_FD) == 0) add_non_blocking_write_fd (inch); } @@ -4620,6 +4645,7 @@ deactivate_process (Lisp_Object proc) close_process_fd (&p->open_fd[i]); inchannel = p->infd; + eassert (inchannel < FD_SETSIZE); if (inchannel >= 0) { p->infd = -1; @@ -4853,6 +4879,7 @@ server_accept_connection (Lisp_Object server, int channel) Lisp_Object name = Fformat (nargs, args); Lisp_Object proc = make_process (name); + eassert (0 <= s && s < FD_SETSIZE); chan_process[s] = proc; fcntl (s, F_SETFL, O_NONBLOCK); @@ -5132,6 +5159,8 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) break; + eassert (max_desc < FD_SETSIZE); + #if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS { Lisp_Object process_list_head, aproc; @@ -5875,6 +5904,7 @@ read_process_output (Lisp_Object proc, int channel) { ssize_t nbytes; struct Lisp_Process *p = XPROCESS (proc); + eassert (0 <= channel && channel < FD_SETSIZE); struct coding_system *coding = proc_decode_coding_system[channel]; int carryover = p->decoding_carryover; ptrdiff_t readmax = clip_to_bounds (1, read_process_output_max, PTRDIFF_MAX); @@ -6039,6 +6069,7 @@ read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars, proc_encode_coding_system[p->outfd] surely points to a valid memory because p->outfd will be changed once EOF is sent to the process. */ + eassert (p->outfd < FD_SETSIZE); if (NILP (p->encode_coding_system) && p->outfd >= 0 && proc_encode_coding_system[p->outfd]) { @@ -6278,6 +6309,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len, if (p->outfd < 0) error ("Output file descriptor of %s is closed", SDATA (p->name)); + eassert (p->outfd < FD_SETSIZE); coding = proc_encode_coding_system[p->outfd]; Vlast_coding_system_used = CODING_ID_NAME (coding->id); @@ -6387,6 +6419,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len, /* Send this batch, using one or more write calls. */ ptrdiff_t written = 0; int outfd = p->outfd; + eassert (0 <= outfd && outfd < FD_SETSIZE); #ifdef DATAGRAM_SOCKETS if (DATAGRAM_CHAN_P (outfd)) { @@ -6837,6 +6870,7 @@ traffic. */) struct Lisp_Process *p; p = XPROCESS (process); + eassert (p->infd < FD_SETSIZE); if (EQ (p->command, Qt) && p->infd >= 0 && (!EQ (p->filter, Qt) || EQ (p->status, Qlisten))) @@ -6964,6 +6998,7 @@ process has been transmitted to the serial port. */) outfd = XPROCESS (proc)->outfd; + eassert (outfd < FD_SETSIZE); if (outfd >= 0) coding = proc_encode_coding_system[outfd]; @@ -7011,11 +7046,13 @@ process has been transmitted to the serial port. */) p->open_fd[WRITE_TO_SUBPROCESS] = new_outfd; p->outfd = new_outfd; + eassert (0 <= new_outfd && new_outfd < FD_SETSIZE); if (!proc_encode_coding_system[new_outfd]) proc_encode_coding_system[new_outfd] = xmalloc (sizeof (struct coding_system)); if (old_outfd >= 0) { + eassert (old_outfd < FD_SETSIZE); *proc_encode_coding_system[new_outfd] = *proc_encode_coding_system[old_outfd]; memset (proc_encode_coding_system[old_outfd], 0, @@ -7464,6 +7501,7 @@ DEFUN ("process-filter-multibyte-p", Fprocess_filter_multibyte_p, struct Lisp_Process *p = XPROCESS (process); if (p->infd < 0) return Qnil; + eassert (p->infd < FD_SETSIZE); struct coding_system *coding = proc_decode_coding_system[p->infd]; return (CODING_FOR_UNIBYTE (coding) ? Qnil : Qt); } @@ -7497,6 +7535,7 @@ keyboard_bit_set (fd_set *mask) { int fd; + eassert (max_desc < FD_SETSIZE); for (fd = 0; fd <= max_desc; fd++) if (FD_ISSET (fd, mask) && ((fd_callback_info[fd].flags & (FOR_READ | KEYBOARD_FD)) @@ -7744,6 +7783,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, void add_timer_wait_descriptor (int fd) { + eassert (0 <= fd && fd < FD_SETSIZE); add_read_fd (fd, timerfd_callback, NULL); fd_callback_info[fd].flags &= ~KEYBOARD_FD; } @@ -7806,6 +7846,7 @@ setup_process_coding_systems (Lisp_Object process) if (inch < 0 || outch < 0) return; + eassert (0 <= inch && inch < FD_SETSIZE); if (!proc_decode_coding_system[inch]) proc_decode_coding_system[inch] = xmalloc (sizeof (struct coding_system)); coding_system = p->decode_coding_system; @@ -7817,6 +7858,7 @@ setup_process_coding_systems (Lisp_Object process) } setup_coding_system (coding_system, proc_decode_coding_system[inch]); + eassert (0 <= outch && outch < FD_SETSIZE); if (!proc_encode_coding_system[outch]) proc_encode_coding_system[outch] = xmalloc (sizeof (struct coding_system)); setup_coding_system (p->encode_coding_system, -- 2.39.5