git: edece33b38eb - main - inpcb: move local address assignment out of in_pcbdisconnect()

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Sun, 12 Apr 2026 18:35:42 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=edece33b38ebcba46bd4d81382ebdb5a634a0c6d

commit edece33b38ebcba46bd4d81382ebdb5a634a0c6d
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2026-04-12 18:34:57 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2026-04-12 18:34:57 +0000

    inpcb: move local address assignment out of in_pcbdisconnect()
    
    The logic of clearing local address at the protocol level makes sense.  It
    is feature of UDP, not of any protocol, that local address is cleared on
    disconnect.  This code can be tracked down to pre-FreeBSD times.
    
    For example, for TCP we want a disconnected socket to return previously
    used local address with getsockname(2).  The TCP has successfully evaded
    that by not calling in_pcbdisconnect() and calling in_pcbdetach() in the
    very old code and in_pcbdrop() later.   After D55661 TCP again has this
    potential bug masked.  Better make it right than rely on such
    unintentional evasions.
    
    The raw IP sockets don't use in_pcbdisconnect(), but they are going to in
    the near future.  If in_pcbdisconnect() clears local address for them,
    that would be a larger bug than just getsockname().  A raw socket may be
    bound with bind(2) and then connect(2)ed, and then disconnected, e.g.
    connect(INADDR_ANY).  And when we run raw IP socket through
    in_pcbdisconnect() we don't want to lose local address.
    
    This reverts D38362.
    This reverts commit 2589ec0f365777faacf36bd1eb24706538836b17.
    
    Reviewed by:            rrs, markj
    Differential Revision:  https://reviews.freebsd.org/D56170
---
 sys/netinet/in_pcb.c       | 1 -
 sys/netinet/udp_usrreq.c   | 3 +++
 sys/netinet6/in6_pcb.c     | 1 -
 sys/netinet6/udp6_usrreq.c | 3 +++
 4 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 29214fbd2cf6..7b294e0a92d5 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1465,7 +1465,6 @@ in_pcbdisconnect(struct inpcb *inp)
 	if ((inp->inp_socket->so_proto->pr_flags & PR_CONNREQUIRED) == 0) {
 		/* See the comment in in_pcbinshash(). */
 		inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
-		inp->inp_laddr.s_addr = INADDR_ANY;
 		inp->inp_faddr.s_addr = INADDR_ANY;
 		inp->inp_fport = 0;
 	}
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index b973b299ca56..23b0ca684b09 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1528,6 +1528,7 @@ udp_abort(struct socket *so)
 	INP_WLOCK(inp);
 	if (inp->inp_faddr.s_addr != INADDR_ANY) {
 		in_pcbdisconnect(inp);
+		inp->inp_laddr.s_addr = INADDR_ANY;
 		soisdisconnected(so);
 	}
 	INP_WUNLOCK(inp);
@@ -1630,6 +1631,7 @@ udp_close(struct socket *so)
 	INP_WLOCK(inp);
 	if (inp->inp_faddr.s_addr != INADDR_ANY) {
 		in_pcbdisconnect(inp);
+		inp->inp_laddr.s_addr = INADDR_ANY;
 		soisdisconnected(so);
 	}
 	INP_WUNLOCK(inp);
@@ -1697,6 +1699,7 @@ udp_disconnect(struct socket *so)
 		return (ENOTCONN);
 	}
 	in_pcbdisconnect(inp);
+	inp->inp_laddr.s_addr = INADDR_ANY;
 	SOCK_LOCK(so);
 	so->so_state &= ~SS_ISCONNECTED;		/* XXX */
 	SOCK_UNLOCK(so);
diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c
index 136f5c8a9828..ad596cf761d2 100644
--- a/sys/netinet6/in6_pcb.c
+++ b/sys/netinet6/in6_pcb.c
@@ -542,7 +542,6 @@ in6_pcbdisconnect(struct inpcb *inp)
 		/* See the comment in in_pcbinshash(). */
 		inp->inp_smr = smr_advance(inp->inp_pcbinfo->ipi_smr);
 		/* XXX-MJ torn writes are visible to SMR lookup */
-		memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
 		memset(&inp->in6p_faddr, 0, sizeof(inp->in6p_faddr));
 		inp->inp_fport = 0;
 	}
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 3f4c3111fa5b..ede791a7d175 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -996,6 +996,7 @@ udp6_abort(struct socket *so)
 
 	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
 		in6_pcbdisconnect(inp);
+		memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
 		soisdisconnected(so);
 	}
 	INP_WUNLOCK(inp);
@@ -1105,6 +1106,7 @@ udp6_close(struct socket *so)
 #endif
 	if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
 		in6_pcbdisconnect(inp);
+		memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
 		soisdisconnected(so);
 	}
 	INP_WUNLOCK(inp);
@@ -1239,6 +1241,7 @@ udp6_disconnect(struct socket *so)
 	}
 
 	in6_pcbdisconnect(inp);
+	memset(&inp->in6p_laddr, 0, sizeof(inp->in6p_laddr));
 	SOCK_LOCK(so);
 	so->so_state &= ~SS_ISCONNECTED;		/* XXX */
 	SOCK_UNLOCK(so);