On Tue, Apr 14, 2026 at 7:13 AM Michal Luczaj <[email protected]> wrote: > > unix_stream_connect() sets sk_state (`WRITE_ONCE(sk->sk_state, > TCP_ESTABLISHED)`) _before_ it assigns a peer (`unix_peer(sk) = newsk`). > sk_state == TCP_ESTABLISHED makes sock_map_sk_state_allowed() believe that > socket is properly set up, which would include having a defined peer. IOW, > there's a window when unix_stream_bpf_update_proto() can be called on > socket which still has unix_peer(sk) == NULL. > > CPU0 bpf CPU1 connect > -------- ------------ > > WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED) > sock_map_sk_state_allowed(sk) > ... > sk_pair = unix_peer(sk) > sock_hold(sk_pair) > sock_hold(newsk) > smp_mb__after_atomic() > unix_peer(sk) = newsk > > BUG: kernel NULL pointer dereference, address: 0000000000000080 > RIP: 0010:unix_stream_bpf_update_proto+0xa0/0x1b0 > Call Trace: > sock_map_link+0x564/0x8b0 > sock_map_update_common+0x6e/0x340 > sock_map_update_elem_sys+0x17d/0x240 > __sys_bpf+0x26db/0x3250 > __x64_sys_bpf+0x21/0x30 > do_syscall_64+0x6b/0x3a0 > entry_SYSCALL_64_after_hwframe+0x76/0x7e > > Initial idea was to move peer assignment _before_ the sk_state update[1], > but that involved an additional memory barrier, and changing the hot path > was rejected. > Then a NULL check during proto update in unix_stream_bpf_update_proto() was > considered[2], but the follow-up discussion[3] focused on the root cause, > i.e. sockmap update taking a wrong lock. Or, more specifically, missing > unix_state_lock()[4]. > In the end it was concluded that teaching sockmap about the af_unix locking > would be unnecessarily complex[5]. > Complexity aside, since BPF_PROG_TYPE_SCHED_CLS and BPF_PROG_TYPE_SCHED_ACT > are allowed to update sockmaps, sock_map_update_elem() taking the unix > lock, as it is currently implemented in unix_state_lock(): > spin_lock(&unix_sk(s)->lock), would be problematic. unix_state_lock() taken > in a process context, followed by a softirq-context TC BPF program > attempting to take the same spinlock -- deadlock[6]. > This way we circled back to the peer check idea[2]. > > [1]: > https://lore.kernel.org/netdev/[email protected]/ > [2]: https://lore.kernel.org/netdev/[email protected]/ > [3]: > https://lore.kernel.org/netdev/[email protected]/ > [4]: > https://lore.kernel.org/netdev/CAAVpQUA+8GL_j63CaKb8hbxoL21izD58yr1NvhOhU=j+35+...@mail.gmail.com/ > [5]: > https://lore.kernel.org/bpf/caavpquahijomext28gi10dslumzgyh+jk61ujn+fz-wvcod...@mail.gmail.com/ > [6]: > https://lore.kernel.org/bpf/[email protected]/ > > Summary of scenarios where af_unix/stream connect() may race a sockmap > update: > > 1. connect() vs. bpf(BPF_MAP_UPDATE_ELEM), i.e. sock_map_update_elem_sys() > > Implemented NULL check is sufficient. Once assigned, socket peer won't > be released until socket fd is released. And that's not an issue because > sock_map_update_elem_sys() bumps fd refcnf. > > 2. connect() vs BPF program doing update > > Update restricted per verifier.c:may_update_sockmap() to > > BPF_PROG_TYPE_TRACING/BPF_TRACE_ITER > BPF_PROG_TYPE_SOCK_OPS (bpf_sock_map_update() only) > BPF_PROG_TYPE_SOCKET_FILTER > BPF_PROG_TYPE_SCHED_CLS > BPF_PROG_TYPE_SCHED_ACT > BPF_PROG_TYPE_XDP > BPF_PROG_TYPE_SK_REUSEPORT > BPF_PROG_TYPE_FLOW_DISSECTOR > BPF_PROG_TYPE_SK_LOOKUP > > Plus one more race to consider: > > CPU0 bpf CPU1 connect > -------- ------------ > > WRITE_ONCE(sk->sk_state, TCP_ESTABLISHED) > sock_map_sk_state_allowed(sk) > sock_hold(newsk) > smp_mb__after_atomic() > unix_peer(sk) = newsk > sk_pair = unix_peer(sk) > if (unlikely(!sk_pair)) > return -EINVAL; > > CPU1 close > ---------- > > skpair = unix_peer(sk); > unix_peer(sk) = NULL; > sock_put(skpair) > // use after free? > sock_hold(sk_pair) > > 2.1 BPF program invoking helper function bpf_sock_map_update() -> > BPF_CALL_4(bpf_sock_map_update(), ...) > > Helper limited to BPF_PROG_TYPE_SOCK_OPS. Nevertheless, a unix sock > might be accessible via bpf_map_lookup_elem(). Which implies sk > already having psock, which in turn implies sk already having > sk_pair. Since sk_psock_destroy() is queued as RCU work, sk_pair > won't go away while BPF executes the update. > > 2.2 BPF program invoking helper function bpf_map_update_elem() -> > sock_map_update_elem() > > 2.2.1 Unix sock accessible to BPF prog only via sockmap lookup in > BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_SCHED_CLS, > BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_XDP, > BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, > BPF_PROG_TYPE_SK_LOOKUP. > > Pretty much the same as case 2.1. > > 2.2.2 Unix sock accessible to BPF program directly: > BPF_PROG_TYPE_TRACING, narrowed down to BPF_TRACE_ITER. > > Sockmap iterator (sock_map_seq_ops) is safe: unix sock > residing in a sockmap means that the sock already went through > the proto update step. > > Unix sock iterator (bpf_iter_unix_seq_ops), on the other hand, > gives access to socks that may still be unconnected. Which > means iterator prog can race sockmap/proto update against > connect(). > > BUG: KASAN: null-ptr-deref in > unix_stream_bpf_update_proto+0x253/0x4d0 > Write of size 4 at addr 0000000000000080 by task test_progs/3140 > Call Trace: > dump_stack_lvl+0x5d/0x80 > kasan_report+0xe4/0x1c0 > kasan_check_range+0x125/0x200 > unix_stream_bpf_update_proto+0x253/0x4d0 > sock_map_link+0x71c/0xec0 > sock_map_update_common+0xbc/0x600 > sock_map_update_elem+0x19a/0x1f0 > bpf_prog_bbbf56096cdd4f01_selective_dump_unix+0x20c/0x217 > bpf_iter_run_prog+0x21e/0xae0 > bpf_iter_unix_seq_show+0x1e0/0x2a0 > bpf_seq_read+0x42c/0x10d0 > vfs_read+0x171/0xb20 > ksys_read+0xff/0x200 > do_syscall_64+0xf7/0x5e0 > entry_SYSCALL_64_after_hwframe+0x76/0x7e > > While the introduced NULL check prevents null-ptr-deref in the > BPF program path as well, it is insufficient to guard against > a poorly timed close() leading to a use-after-free. This will > be addressed in a subsequent patch. > > Reported-by: Michal Luczaj <[email protected]> > Closes: > https://lore.kernel.org/netdev/[email protected]/ > Reported-by: 钱一铭 <[email protected]> > Suggested-by: Kuniyuki Iwashima <[email protected]> > Suggested-by: Martin KaFai Lau <[email protected]> > Fixes: c63829182c37 ("af_unix: Implement ->psock_update_sk_prot()") > Signed-off-by: Michal Luczaj <[email protected]>
Reviewed-by: Kuniyuki Iwashima <[email protected]>

