summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2025-01-09 16:47:25 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2025-01-09 16:47:25 +0000
commit2befe21b3a9527cf7e7259e1735254d9c8fb5a7a (patch)
tree4913d4ba81da8b99b3d3f2af506e948ed00f38ac
parent48991c3b2faa43a8532047d9d365cb4216bf2c26 (diff)
Run TCP sysctl ident and drop with shared net lock.
Convert exclusive net lock for TCPCTL_IDENT and TCPCTL_DROP to shared net lock and push it down into tcp_ident(). Grab the socket lock there with in_pcbsolock_ref(). Move socket release from in_pcbsolock() to in_pcbsounlock_rele() and add _ref and _rele suffix to the inpcb socket lock functions. They both lock and refcount now. in_pcbsounlock_rele() ignores NULL sockets to make the unlock path in error case simpler. Socket lock also protects tcp_drop() and tcp_close() now, so the socket pointer from incpb may be NULL during unlock. In tcp_ident() improve consistency check of address family. OK mvs@
-rw-r--r--sys/netinet/in_pcb.c14
-rw-r--r--sys/netinet/in_pcb.h6
-rw-r--r--sys/netinet/tcp_input.c6
-rw-r--r--sys/netinet/tcp_usrreq.c71
4 files changed, 59 insertions, 38 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 2894276ee6e..61525bded00 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.c,v 1.309 2025/01/03 12:56:15 mvs Exp $ */
+/* $OpenBSD: in_pcb.c,v 1.310 2025/01/09 16:47:24 bluhm Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@@ -623,7 +623,7 @@ in_pcbdetach(struct inpcb *inp)
}
struct socket *
-in_pcbsolock(struct inpcb *inp)
+in_pcbsolock_ref(struct inpcb *inp)
{
struct socket *so;
@@ -634,18 +634,18 @@ in_pcbsolock(struct inpcb *inp)
mtx_leave(&inp->inp_sofree_mtx);
if (so == NULL)
return NULL;
-
rw_enter_write(&so->so_lock);
- sorele(so);
-
return so;
}
void
-in_pcbsounlock(struct inpcb *inp, struct socket *so)
+in_pcbsounlock_rele(struct inpcb *inp, struct socket *so)
{
- KASSERT(inp->inp_socket == so);
+ if (so == NULL)
+ return;
+ KASSERT(inp->inp_socket == NULL || inp->inp_socket == so);
rw_exit_write(&so->so_lock);
+ sorele(so);
}
struct inpcb *
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 36011c6c4f0..56adec0c6bf 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.h,v 1.163 2025/01/05 12:10:39 bluhm Exp $ */
+/* $OpenBSD: in_pcb.h,v 1.164 2025/01/09 16:47:24 bluhm Exp $ */
/* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */
/*
@@ -311,8 +311,8 @@ int in_pcbaddrisavail(const struct inpcb *, struct sockaddr_in *, int,
int in_pcbconnect(struct inpcb *, struct mbuf *);
void in_pcbdetach(struct inpcb *);
struct socket *
- in_pcbsolock(struct inpcb *);
-void in_pcbsounlock(struct inpcb *, struct socket *);
+ in_pcbsolock_ref(struct inpcb *);
+void in_pcbsounlock_rele(struct inpcb *, struct socket *);
struct inpcb *
in_pcbref(struct inpcb *);
void in_pcbunref(struct inpcb *);
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index 0ecd7e0f990..715b5e00506 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_input.c,v 1.420 2025/01/05 12:18:48 bluhm Exp $ */
+/* $OpenBSD: tcp_input.c,v 1.421 2025/01/09 16:47:24 bluhm Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
@@ -3410,7 +3410,7 @@ syn_cache_timer(void *arg)
mtx_leave(&syn_cache_mtx);
NET_LOCK_SHARED();
- so = in_pcbsolock(inp);
+ so = in_pcbsolock_ref(inp);
if (so != NULL) {
now = tcp_now();
#ifdef TCP_ECN
@@ -3418,8 +3418,8 @@ syn_cache_timer(void *arg)
#endif
(void) syn_cache_respond(sc, NULL, now, do_ecn);
tcpstat_inc(tcps_sc_retransmitted);
- in_pcbsounlock(inp, so);
}
+ in_pcbsounlock_rele(inp, so);
NET_UNLOCK_SHARED();
in_pcbunref(inp);
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 9a8a12098da..bc40c35242e 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_usrreq.c,v 1.238 2025/01/05 12:23:38 bluhm Exp $ */
+/* $OpenBSD: tcp_usrreq.c,v 1.239 2025/01/09 16:47:24 bluhm Exp $ */
/* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */
/*
@@ -1122,15 +1122,13 @@ tcp_ident(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int dodrop)
int error = 0;
struct tcp_ident_mapping tir;
struct inpcb *inp;
- struct tcpcb *tp = NULL;
+ struct socket *so = NULL;
struct sockaddr_in *fin, *lin;
#ifdef INET6
struct sockaddr_in6 *fin6, *lin6;
struct in6_addr f6, l6;
#endif
- NET_ASSERT_LOCKED();
-
if (dodrop) {
if (oldp != NULL || *oldlenp != 0)
return (EINVAL);
@@ -1150,25 +1148,41 @@ tcp_ident(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int dodrop)
if ((error = copyin(oldp, &tir, sizeof (tir))) != 0 )
return (error);
}
+
+ NET_LOCK_SHARED();
+
switch (tir.faddr.ss_family) {
#ifdef INET6
case AF_INET6:
+ if (tir.laddr.ss_family != AF_INET6) {
+ NET_UNLOCK_SHARED();
+ return (EAFNOSUPPORT);
+ }
fin6 = (struct sockaddr_in6 *)&tir.faddr;
error = in6_embedscope(&f6, fin6, NULL, NULL);
- if (error)
+ if (error) {
+ NET_UNLOCK_SHARED();
return EINVAL; /*?*/
+ }
lin6 = (struct sockaddr_in6 *)&tir.laddr;
error = in6_embedscope(&l6, lin6, NULL, NULL);
- if (error)
+ if (error) {
+ NET_UNLOCK_SHARED();
return EINVAL; /*?*/
+ }
break;
#endif
case AF_INET:
+ if (tir.laddr.ss_family != AF_INET) {
+ NET_UNLOCK_SHARED();
+ return (EAFNOSUPPORT);
+ }
fin = (struct sockaddr_in *)&tir.faddr;
lin = (struct sockaddr_in *)&tir.laddr;
break;
default:
- return (EINVAL);
+ NET_UNLOCK_SHARED();
+ return (EAFNOSUPPORT);
}
switch (tir.faddr.ss_family) {
@@ -1187,11 +1201,20 @@ tcp_ident(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int dodrop)
}
if (dodrop) {
- if (inp && (tp = intotcpcb(inp)) &&
- ((inp->inp_socket->so_options & SO_ACCEPTCONN) == 0))
+ struct tcpcb *tp = NULL;
+
+ if (inp != NULL) {
+ so = in_pcbsolock_ref(inp);
+ if (so != NULL)
+ tp = intotcpcb(inp);
+ }
+ if (tp != NULL && !ISSET(so->so_options, SO_ACCEPTCONN))
tp = tcp_drop(tp, ECONNABORTED);
else
error = ESRCH;
+
+ in_pcbsounlock_rele(inp, so);
+ NET_UNLOCK_SHARED();
in_pcbunref(inp);
return (error);
}
@@ -1212,18 +1235,23 @@ tcp_ident(void *oldp, size_t *oldlenp, void *newp, size_t newlen, int dodrop)
}
}
- if (inp != NULL && (inp->inp_socket->so_state & SS_CONNECTOUT)) {
- tir.ruid = inp->inp_socket->so_ruid;
- tir.euid = inp->inp_socket->so_euid;
+ if (inp != NULL)
+ so = in_pcbsolock_ref(inp);
+
+ if (so != NULL && ISSET(so->so_state, SS_CONNECTOUT)) {
+ tir.ruid = so->so_ruid;
+ tir.euid = so->so_euid;
} else {
tir.ruid = -1;
tir.euid = -1;
}
- *oldlenp = sizeof (tir);
- error = copyout((void *)&tir, oldp, sizeof (tir));
+ in_pcbsounlock_rele(inp, so);
+ NET_UNLOCK_SHARED();
in_pcbunref(inp);
- return (error);
+
+ *oldlenp = sizeof(tir);
+ return copyout(&tir, oldp, sizeof(tir));
}
int
@@ -1428,16 +1456,10 @@ tcp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
return (error);
case TCPCTL_IDENT:
- NET_LOCK();
- error = tcp_ident(oldp, oldlenp, newp, newlen, 0);
- NET_UNLOCK();
- return (error);
+ return tcp_ident(oldp, oldlenp, newp, newlen, 0);
case TCPCTL_DROP:
- NET_LOCK();
- error = tcp_ident(oldp, oldlenp, newp, newlen, 1);
- NET_UNLOCK();
- return (error);
+ return tcp_ident(oldp, oldlenp, newp, newlen, 1);
case TCPCTL_REASS_LIMIT:
NET_LOCK();
@@ -1506,9 +1528,8 @@ tcp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
return (error);
default:
- error = sysctl_bounded_arr(tcpctl_vars, nitems(tcpctl_vars),
+ return sysctl_bounded_arr(tcpctl_vars, nitems(tcpctl_vars),
name, namelen, oldp, oldlenp, newp, newlen);
- return (error);
}
/* NOTREACHED */
}