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]> --- bsd/sys/kern/uipc_socket.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/bsd/sys/kern/uipc_socket.cc b/bsd/sys/kern/uipc_socket.cc index 1c49efc..194e74d 100644 --- a/bsd/sys/kern/uipc_socket.cc +++ b/bsd/sys/kern/uipc_socket.cc @@ -3469,11 +3469,19 @@ 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; @@ -3481,8 +3489,25 @@ soisdisconnected(struct socket *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); + } } /* -- 2.7.4 -- 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.
