summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python
diff options
context:
space:
mode:
authorMichal Luczaj <mhal@rbox.co>2026-04-14 16:13:18 +0200
committerMartin KaFai Lau <martin.lau@kernel.org>2026-04-15 17:22:58 -0700
commitdca38b7734d2ea00af4818ff3ae836fab33d5d5a (patch)
tree0b2ab9802303ab31cd19a77132b3f8da71c5c116 /tools/perf/scripts/python
parent997b8483d44c60805c71a9882376a16eb176cb24 (diff)
bpf, sockmap: Fix af_unix null-ptr-deref in proto update
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/ba5c50aa-1df4-40c2-ab33-a72022c5a32e@rbox.co/ [2]: https://lore.kernel.org/netdev/20240610174906.32921-1-kuniyu@amazon.com/ [3]: https://lore.kernel.org/netdev/7603c0e6-cd5b-452b-b710-73b64bd9de26@linux.dev/ [4]: https://lore.kernel.org/netdev/CAAVpQUA+8GL_j63CaKb8hbxoL21izD58yr1NvhOhU=j+35+3og@mail.gmail.com/ [5]: https://lore.kernel.org/bpf/CAAVpQUAHijOMext28Gi10dSLuMzGYh+jK61Ujn+fZ-wvcODR2A@mail.gmail.com/ [6]: https://lore.kernel.org/bpf/dd043c69-4d03-46fe-8325-8f97101435cf@linux.dev/ 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. Fixes: c63829182c37 ("af_unix: Implement ->psock_update_sk_prot()") Closes: https://lore.kernel.org/netdev/ba5c50aa-1df4-40c2-ab33-a72022c5a32e@rbox.co/ Reported-by: Michal Luczaj <mhal@rbox.co> Reported-by: 钱一铭 <yimingqian591@gmail.com> Suggested-by: Kuniyuki Iwashima <kuniyu@google.com> Suggested-by: Martin KaFai Lau <martin.lau@linux.dev> Signed-off-by: Michal Luczaj <mhal@rbox.co> Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260414-unix-proto-update-null-ptr-deref-v4-4-2af6fe97918e@rbox.co
Diffstat (limited to 'tools/perf/scripts/python')
0 files changed, 0 insertions, 0 deletions