aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2021-09-17 16:26:06 +0000
committerMark Johnston <markj@FreeBSD.org>2021-09-24 13:01:51 +0000
commite9e4f8092c2e0ccac922f930072cd14b22ee7c1c (patch)
tree87c7592295523c71201c23cc4d9d4d6a442e2e6b
parente68465ecbf7371efdab5a7d91838e33f4e5e177b (diff)
socket: Fix a use-after-free in soclose()
After releasing the fd reference to a socket "so", we should avoid testing SOLISTENING(so) since the socket may have been freed. Instead, directly test whether the list of unaccepted sockets is empty. Fixes: f4bb1869ddd2 ("Consistently use the SOLISTENING() macro") Pointy hat: markj Sponsored by: The FreeBSD Foundation (cherry picked from commit dfcef8771484271f2bccdb1dbc088a838441c5a7)
-rw-r--r--sys/kern/uipc_socket.c24
1 files changed, 10 insertions, 14 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index dc7407808535..13482fce5980 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1176,6 +1176,7 @@ int
soclose(struct socket *so)
{
struct accept_queue lqueue;
+ struct socket *sp, *tsp;
int error = 0;
KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter"));
@@ -1210,11 +1211,9 @@ drop:
if (so->so_proto->pr_usrreqs->pru_close != NULL)
(*so->so_proto->pr_usrreqs->pru_close)(so);
+ TAILQ_INIT(&lqueue);
SOCK_LOCK(so);
if (SOLISTENING(so)) {
- struct socket *sp;
-
- TAILQ_INIT(&lqueue);
TAILQ_SWAP(&lqueue, &so->sol_incomp, socket, so_list);
TAILQ_CONCAT(&lqueue, &so->sol_comp, so_list);
@@ -1232,17 +1231,14 @@ drop:
KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF"));
so->so_state |= SS_NOFDREF;
sorele(so);
- if (SOLISTENING(so)) {
- struct socket *sp, *tsp;
-
- TAILQ_FOREACH_SAFE(sp, &lqueue, so_list, tsp) {
- SOCK_LOCK(sp);
- if (sp->so_count == 0) {
- SOCK_UNLOCK(sp);
- soabort(sp);
- } else
- /* sp is now in sofree() */
- SOCK_UNLOCK(sp);
+ TAILQ_FOREACH_SAFE(sp, &lqueue, so_list, tsp) {
+ SOCK_LOCK(sp);
+ if (refcount_load(&sp->so_count) == 0) {
+ SOCK_UNLOCK(sp);
+ soabort(sp);
+ } else {
+ /* sp is now in sofree() */
+ SOCK_UNLOCK(sp);
}
}
CURVNET_RESTORE();