From: Timmons C. Player <[email protected]> Committer: Nadav Har'El <[email protected]> Branch: master
uipc_socket.cc: prevent use after free bug in soisdisconnected Simultaneously closing a socket from both the network and user space sides can trigger a use after free bug in soisdisconnected. This change uses the existing socket reference counting mechanism to prevent the socket from being freed until after this function has completed. Signed-off-by: Timmons C. Player <[email protected]> Message-Id: <[email protected]> --- diff --git a/bsd/sys/kern/uipc_socket.cc b/bsd/sys/kern/uipc_socket.cc --- a/bsd/sys/kern/uipc_socket.cc +++ b/bsd/sys/kern/uipc_socket.cc @@ -3469,20 +3469,45 @@ void soisdisconnected(struct socket *so) { + bool do_release = false; /* * Note: This code assumes that SOCK_LOCK(so) and * SOCKBUF_LOCK(&so->so_rcv) are the same. */ SOCK_LOCK(so); + /* + * If user space has already closed the socket, then it's possible + * for some of these wakeups to trigger soclose. We need to prevent + * the socket from getting freed in the middle of this function, so + * bump the reference count. + */ + soref(so); so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); so->so_state |= SS_ISDISCONNECTED; so->so_rcv.sb_state |= SBS_CANTRCVMORE; sorwakeup_locked(so); so->so_snd.sb_state |= SBS_CANTSENDMORE; sbdrop_locked(so, &so->so_snd, so->so_snd.sb_cc); sowwakeup_locked(so); + /* + * If we have the only reference, then we need to call sorele to + * free the socket. If not, then we just quietly drop the ref + * count ourselves to avoid taking the accept lock and possibly + * deadlocking. + */ + if (so->so_count == 1) { + do_release = true; + } else { + so->so_count--; + } SOCK_UNLOCK(so); wakeup(&so->so_timeo); + + if (do_release) { + ACCEPT_LOCK(); + SOCK_LOCK(so); + sorele(so); + } } /* -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
