From 93998d166fb7907a6ae16a08d6831e31f656c97b Mon Sep 17 00:00:00 2001 From: Mark Johnston Date: Sun, 23 Apr 2023 10:36:24 -0400 Subject: [PATCH] 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);