git: 93998d166fb7 - main - inpcb: Fix some bugs in _in_pcbinshash_wild()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Sun, 23 Apr 2023 17:56:04 UTC
The branch main has been updated by markj:

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

commit 93998d166fb7907a6ae16a08d6831e31f656c97b
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-04-23 14:36:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-04-23 17:55:57 +0000

    inpcb: Fix some bugs in _in_pcbinshash_wild()
    
    - In _in_pcbinshash_wild(), we should avoid returning v6 sockets unless
      no other matches are available.  This preserves pre-existing
      semantics.
    - Fix an inverted test: when inserting a non-jailed PCB, we want to
      search for the first non-jailed PCB in the hash chain.
    - Test the right PCB when searching for a non-jailed PCB.
    
    While here, add a required locking assertion.
    
    Fixes:  7b92493ab1d4 ("inpcb: Avoid inp_cred dereferences in SMR-protected lookup")
---
 sys/netinet/in_pcb.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 4c1ba835a066..9193dfb2372b 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -2506,7 +2506,8 @@ in_pcbjailed(const struct inpcb *inp, unsigned int flag)
  * in_pcblookup_hash_wild_*() always encounter the highest-ranking PCB first.
  *
  * Specifically, keep jailed PCBs in front of non-jailed PCBs, and keep PCBs
- * with exact local addresses ahead of wildcard PCBs.
+ * with exact local addresses ahead of wildcard PCBs.  Unbound v4-mapped v6 PCBs
+ * always appear last no matter whether they are jailed.
  */
 static void
 _in_pcbinshash_wild(struct inpcbhead *pcbhash, struct inpcb *inp)
@@ -2514,14 +2515,26 @@ _in_pcbinshash_wild(struct inpcbhead *pcbhash, struct inpcb *inp)
 	struct inpcb *last;
 	bool bound, injail;
 
+	INP_LOCK_ASSERT(inp);
 	INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
 
 	last = NULL;
 	bound = inp->inp_laddr.s_addr != INADDR_ANY;
+	if (!bound && (inp->inp_vflag & INP_IPV6PROTO) != 0) {
+		CK_LIST_FOREACH(last, pcbhash, inp_hash_wild) {
+			if (CK_LIST_NEXT(last, inp_hash_wild) == NULL) {
+				CK_LIST_INSERT_AFTER(last, inp, inp_hash_wild);
+				return;
+			}
+		}
+		CK_LIST_INSERT_HEAD(pcbhash, inp, inp_hash_wild);
+		return;
+	}
+
 	injail = in_pcbjailed(inp, PR_IP4);
 	if (!injail) {
 		CK_LIST_FOREACH(last, pcbhash, inp_hash_wild) {
-			if (in_pcbjailed(inp, PR_IP4))
+			if (!in_pcbjailed(last, PR_IP4))
 				break;
 			if (CK_LIST_NEXT(last, inp_hash_wild) == NULL) {
 				CK_LIST_INSERT_AFTER(last, inp, inp_hash_wild);
@@ -2559,6 +2572,7 @@ _in6_pcbinshash_wild(struct inpcbhead *pcbhash, struct inpcb *inp)
 	struct inpcb *last;
 	bool bound, injail;
 
+	INP_LOCK_ASSERT(inp);
 	INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
 
 	last = NULL;
@@ -2566,7 +2580,7 @@ _in6_pcbinshash_wild(struct inpcbhead *pcbhash, struct inpcb *inp)
 	injail = in_pcbjailed(inp, PR_IP6);
 	if (!injail) {
 		CK_LIST_FOREACH(last, pcbhash, inp_hash_wild) {
-			if (in_pcbjailed(last, PR_IP6))
+			if (!in_pcbjailed(last, PR_IP6))
 				break;
 			if (CK_LIST_NEXT(last, inp_hash_wild) == NULL) {
 				CK_LIST_INSERT_AFTER(last, inp, inp_hash_wild);