aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2026-06-15 15:52:24 +0000
committerMark Johnston <markj@FreeBSD.org>2026-06-30 02:32:57 +0000
commit7b3373d4eb5f86345e743e1233866fa7b0ff6160 (patch)
tree4fcb1970a94cb7cca5074a1fc6c0e5d828abff04
parentb77d19dcc0d5ff8fc704ae255630b92b7a91f05d (diff)
rpcsec_tls: Avoid a socket reference underflow in rpctls_server()
The upcall_sockets tree owns a ref on any resident socket. When a socket is removed after a TLS handshake failure, rpctls_rpc_failed() thus calls soclose(). rpctls_server() does not acquire an extra ref to compensate for this. So, if the upcall fails, e.g., because rpc.tlsservd is not running, we'll call soclose() to drop the reference, but this effectively releases the xprt layer's reference. Fix the problem by explicitly acquiring a socket reference when adding a socket to the upcall tree. Approved by: so Security: FreeBSD-EN-26:17.rpcsec_tls PR: 289734 Reviewed by: rmacklem, glebius MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D57555 (cherry picked from commit fc7993cf2d6ddba9f94683565838bf8fabc0145c) (cherry picked from commit f3b14134dec11c2f09980f25c4c60accbfea0f1e)
-rw-r--r--sys/rpc/rpcsec_tls/rpctls_impl.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/sys/rpc/rpcsec_tls/rpctls_impl.c b/sys/rpc/rpcsec_tls/rpctls_impl.c
index 51fe270b13d9..4d5f30b7a60a 100644
--- a/sys/rpc/rpcsec_tls/rpctls_impl.c
+++ b/sys/rpc/rpcsec_tls/rpctls_impl.c
@@ -190,7 +190,6 @@ sys_rpctls_syscall(struct thread *td, struct rpctls_syscall_args *uap)
KRPC_CURVNET_RESTORE();
return (error);
}
- soref(ups.so);
if (ups.server) {
/*
* Once this file descriptor is associated
@@ -277,6 +276,7 @@ rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
if (stat != RPC_SUCCESS)
return (RPC_SYSTEMERROR);
+ soref(so);
mtx_lock(&rpctls_lock);
RB_INSERT(upsock_t, &upcall_sockets, &ups);
mtx_unlock(&rpctls_lock);
@@ -294,9 +294,16 @@ rpctls_connect(CLIENT *newclient, char *certname, struct socket *so,
stat = rpctlscd_connect_2(&arg, &res, rpctls_connect_handle);
if (stat == RPC_SUCCESS)
*reterr = res.reterr;
- else
+ else {
rpctls_rpc_failed(&ups, so);
+ /*
+ * The socket was closed, make sure the krpc code doesn't close
+ * it a second time.
+ */
+ CLNT_CONTROL(newclient, CLSET_TLS, &(int){RPCTLS_INHANDSHAKE});
+ }
+
/* Unblock reception. */
CLNT_CONTROL(newclient, CLSET_BLOCKRCV, &(int){0});
@@ -388,6 +395,7 @@ rpctls_server(SVCXPRT *xprt, uint32_t *flags, uid_t *uid, int *ngrps,
uint32_t *gidv;
int i;
+ soref(xprt->xp_socket);
mtx_lock(&rpctls_lock);
RB_INSERT(upsock_t, &upcall_sockets, &ups);
mtx_unlock(&rpctls_lock);
@@ -407,9 +415,18 @@ rpctls_server(SVCXPRT *xprt, uint32_t *flags, uid_t *uid, int *ngrps,
for (i = 0; i < *ngrps; i++)
*gidp++ = *gidv++;
}
- } else
+ } else {
rpctls_rpc_failed(&ups, xprt->xp_socket);
+ /*
+ * The socket was closed, make sure the krpc code doesn't close
+ * it a second time.
+ */
+ sx_xlock(&ups.xp->xp_lock);
+ ups.xp->xp_tls = RPCTLS_FLAGS_HANDSHFAIL;
+ sx_xunlock(&ups.xp->xp_lock);
+ }
+
mem_free(res.gid.gid_val, 0);
#ifdef INVARIANTS