From f87ede57a2e97f0743a85b94072c7163fa485ae9 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 15 Jan 2015 14:13:19 +0100 Subject: libnetworking: Fix close of active sockets Send a special event to notify tasks waiting for a socket state change in case this socket gets closed. This prevents a use after free. Close #785. --- cpukit/libnetworking/kern/uipc_socket.c | 27 +++++- cpukit/libnetworking/rtems/rtems_bsdnet_internal.h | 2 +- cpukit/libnetworking/rtems/rtems_glue.c | 102 ++++++++++----------- cpukit/libnetworking/rtems/rtems_select.c | 7 +- cpukit/libnetworking/rtems/rtems_syscall.c | 22 +++-- cpukit/rtems/include/rtems/rtems/event.h | 5 + 6 files changed, 99 insertions(+), 66 deletions(-) (limited to 'cpukit') diff --git a/cpukit/libnetworking/kern/uipc_socket.c b/cpukit/libnetworking/kern/uipc_socket.c index 7ed3ad0d75..b221a377ca 100644 --- a/cpukit/libnetworking/kern/uipc_socket.c +++ b/cpukit/libnetworking/kern/uipc_socket.c @@ -145,6 +145,26 @@ sofree(struct socket *so) FREE(so, M_SOCKET); } +static void +rtems_socket_close_notify(struct socket *so) +{ + if (so->so_pgid) { + rtems_event_system_send(so->so_pgid, RTEMS_EVENT_SYSTEM_NETWORK_CLOSE); + } +} + +static void +rtems_sockbuf_close_notify(struct socket *so, struct sockbuf *sb) +{ + if (sb->sb_flags & SB_WAIT) { + rtems_event_system_send(sb->sb_sel.si_pid, + RTEMS_EVENT_SYSTEM_NETWORK_CLOSE); + } + + if (sb->sb_wakeup) + (*sb->sb_wakeup)(so, sb->sb_wakeuparg); +} + /* * Close a socket on last file table reference removal. * Initiate disconnect if connected. @@ -156,6 +176,10 @@ soclose(struct socket *so) int s = splnet(); /* conservative */ int error = 0; + rtems_socket_close_notify(so); + rtems_sockbuf_close_notify(so, &so->so_snd); + rtems_sockbuf_close_notify(so, &so->so_rcv); + if (so->so_options & SO_ACCEPTCONN) { struct socket *sp, *sonext; @@ -742,7 +766,8 @@ dontblock: break; error = sbwait(&so->so_rcv); if (error) { - sbunlock(&so->so_rcv); + if (error != ENXIO) + sbunlock(&so->so_rcv); splx(s); return (0); } diff --git a/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h b/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h index 5be781b4c8..b790f0561d 100644 --- a/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h +++ b/cpukit/libnetworking/rtems/rtems_bsdnet_internal.h @@ -219,7 +219,7 @@ int ioctl (int, ioctl_command_t, ...); #define NETISR_IP_EVENT (1L << NETISR_IP) #define NETISR_ARP_EVENT (1L << NETISR_ARP) #define NETISR_EVENTS (NETISR_IP_EVENT|NETISR_ARP_EVENT) -#if (SBWAIT_EVENT & SOSLEEP_EVENT & NETISR_EVENTS) +#if (SBWAIT_EVENT & SOSLEEP_EVENT & NETISR_EVENTS & RTEMS_EVENT_SYSTEM_NETWORK_CLOSE) # error "Network event conflict" #endif diff --git a/cpukit/libnetworking/rtems/rtems_glue.c b/cpukit/libnetworking/rtems/rtems_glue.c index 4c90a98a75..9d122e46a5 100644 --- a/cpukit/libnetworking/rtems/rtems_glue.c +++ b/cpukit/libnetworking/rtems/rtems_glue.c @@ -432,47 +432,68 @@ rtems_bsdnet_semaphore_release (void) #endif } -/* - * Wait for something to happen to a socket buffer - */ -int -sbwait(struct sockbuf *sb) +static int +rtems_bsdnet_sleep(rtems_event_set in, rtems_interval ticks) { - rtems_event_set events; - rtems_id tid; rtems_status_code sc; + rtems_event_set out; + rtems_event_set out2; + + in |= RTEMS_EVENT_SYSTEM_NETWORK_CLOSE; /* - * Soak up any pending events. - * The sleep/wakeup synchronization in the FreeBSD - * kernel has no memory. + * Soak up any pending events. The sleep/wakeup synchronization in the + * FreeBSD kernel has no memory. */ - rtems_event_system_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &events); + rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT, &out); /* - * Set this task as the target of the wakeup operation. + * Wait for the wakeup event. */ - rtems_task_ident (RTEMS_SELF, 0, &tid); - sb->sb_sel.si_pid = tid; + sc = rtems_bsdnet_event_receive(in, RTEMS_EVENT_ANY | RTEMS_WAIT, + ticks, &out); /* - * Show that socket is waiting + * Get additional events that may have been received between the + * rtems_event_system_receive() and the rtems_bsdnet_semaphore_obtain(). */ - sb->sb_flags |= SB_WAIT; + rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT, &out2); + out |= out2; + + if (out & RTEMS_EVENT_SYSTEM_NETWORK_CLOSE) + return (ENXIO); + + if (sc == RTEMS_SUCCESSFUL) + return (0); + + return (EWOULDBLOCK); +} + +/* + * Wait for something to happen to a socket buffer + */ +int +sbwait(struct sockbuf *sb) +{ + int error; /* - * Wait for the wakeup event. + * Set this task as the target of the wakeup operation. */ - sc = rtems_bsdnet_event_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, sb->sb_timeo, &events); + sb->sb_sel.si_pid = rtems_task_self(); /* - * Return the status of the wait. + * Show that socket is waiting */ - switch (sc) { - case RTEMS_SUCCESSFUL: return 0; - case RTEMS_TIMEOUT: return EWOULDBLOCK; - default: return ENXIO; - } + sb->sb_flags |= SB_WAIT; + + error = rtems_bsdnet_sleep(SBWAIT_EVENT, sb->sb_timeo); + if (error != ENXIO) + sb->sb_flags &= ~SB_WAIT; + + return (error); } @@ -485,7 +506,6 @@ sowakeup( struct sockbuf *sb) { if (sb->sb_flags & SB_WAIT) { - sb->sb_flags &= ~SB_WAIT; rtems_event_system_send (sb->sb_sel.si_pid, SBWAIT_EVENT); } if (sb->sb_wakeup) { @@ -514,40 +534,20 @@ wakeup (void *p) int soconnsleep (struct socket *so) { - rtems_event_set events; - rtems_id tid; - rtems_status_code sc; - - /* - * Soak up any pending events. - * The sleep/wakeup synchronization in the FreeBSD - * kernel has no memory. - */ - rtems_event_system_receive (SOSLEEP_EVENT, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &events); + int error; /* * Set this task as the target of the wakeup operation. */ if (so->so_pgid) rtems_panic ("Another task is already sleeping on that socket"); - rtems_task_ident (RTEMS_SELF, 0, &tid); - so->so_pgid = tid; - - /* - * Wait for the wakeup event. - */ - sc = rtems_bsdnet_event_receive (SOSLEEP_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, so->so_rcv.sb_timeo, &events); + so->so_pgid = rtems_task_self(); - /* - * Relinquish ownership of the socket. - */ - so->so_pgid = 0; + error = rtems_bsdnet_sleep(SOSLEEP_EVENT, so->so_rcv.sb_timeo); + if (error != ENXIO) + so->so_pgid = 0; - switch (sc) { - case RTEMS_SUCCESSFUL: return 0; - case RTEMS_TIMEOUT: return EWOULDBLOCK; - default: return ENXIO; - } + return (error); } /* diff --git a/cpukit/libnetworking/rtems/rtems_select.c b/cpukit/libnetworking/rtems/rtems_select.c index 47a4cb2982..05c8951cd9 100644 --- a/cpukit/libnetworking/rtems/rtems_select.c +++ b/cpukit/libnetworking/rtems/rtems_select.c @@ -119,7 +119,8 @@ select (int nfds, fd_set *__restrict readfds, fd_set *__restrict writefds, int retval = 0; rtems_id tid; rtems_interval then = 0, now; - rtems_event_set events; + rtems_event_set in = SBWAIT_EVENT | RTEMS_EVENT_SYSTEM_NETWORK_CLOSE; + rtems_event_set out; if (nfds < 0) return (EINVAL); @@ -145,7 +146,7 @@ select (int nfds, fd_set *__restrict readfds, fd_set *__restrict writefds, #undef getbits rtems_task_ident (RTEMS_SELF, 0, &tid); - rtems_event_system_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &events); + rtems_event_system_receive (in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &out); for (;;) { rtems_bsdnet_semaphore_obtain (); error = selscan(tid, ibits, obits, nfds, &retval); @@ -159,7 +160,7 @@ select (int nfds, fd_set *__restrict readfds, fd_set *__restrict writefds, break; then = now; } - rtems_event_system_receive (SBWAIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, timo, &events); + rtems_event_system_receive (in, RTEMS_EVENT_ANY | RTEMS_WAIT, timo, &out); } #define putbits(name,i) if (name) *name = ob[i] diff --git a/cpukit/libnetworking/rtems/rtems_syscall.c b/cpukit/libnetworking/rtems/rtems_syscall.c index 324a634426..106379805e 100644 --- a/cpukit/libnetworking/rtems/rtems_syscall.c +++ b/cpukit/libnetworking/rtems/rtems_syscall.c @@ -204,17 +204,17 @@ connect (int s, struct sockaddr *name, int namelen) rtems_bsdnet_semaphore_release (); return -1; } - while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { + error = so->so_error; + while (error == 0 && (so->so_state & SS_ISCONNECTING)) { error = soconnsleep (so); if (error) break; - } - if (error == 0) { error = so->so_error; so->so_error = 0; } bad: - so->so_state &= ~SS_ISCONNECTING; + if (error != ENXIO) + so->so_state &= ~SS_ISCONNECTING; m_freem (nam); if (error) errno = error; @@ -249,6 +249,7 @@ accept (int s, struct sockaddr *name, int *namelen) int fd; struct socket *head, *so; struct mbuf *nam; + int error; rtems_bsdnet_semaphore_obtain (); if ((head = rtems_bsdnet_fdToSocket (s)) == NULL) { @@ -265,16 +266,16 @@ accept (int s, struct sockaddr *name, int *namelen) rtems_bsdnet_semaphore_release (); return -1; } - while (head->so_comp.tqh_first == NULL && head->so_error == 0) { + error = head->so_error; + while (error == 0 && head->so_comp.tqh_first == NULL) { if (head->so_state & SS_CANTRCVMORE) { - head->so_error = ECONNABORTED; + error = ECONNABORTED; break; } - head->so_error = soconnsleep (head); + error = soconnsleep (head); } - if (head->so_error) { - errno = head->so_error; - head->so_error = 0; + if (error) { + errno = error; rtems_bsdnet_semaphore_release (); return -1; } @@ -715,6 +716,7 @@ rtems_bsdnet_close (rtems_libio_t *iop) rtems_bsdnet_semaphore_release (); return -1; } + iop->data1 = NULL; error = soclose (so); rtems_bsdnet_semaphore_release (); if (error) { diff --git a/cpukit/rtems/include/rtems/rtems/event.h b/cpukit/rtems/include/rtems/rtems/event.h index ca48ef27da..451a313482 100644 --- a/cpukit/rtems/include/rtems/rtems/event.h +++ b/cpukit/rtems/include/rtems/rtems/event.h @@ -313,6 +313,11 @@ rtems_status_code rtems_event_receive ( */ #define RTEMS_EVENT_SYSTEM_NETWORK_SOSLEEP RTEMS_EVENT_25 +/** + * @brief Reserved system event for network socket close. + */ +#define RTEMS_EVENT_SYSTEM_NETWORK_CLOSE RTEMS_EVENT_26 + /** * @brief Reserved system event for transient usage. */ -- cgit v1.2.3