CVE-2026-64352

Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
25/07/2026
Last modified:
25/07/2026

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> bpf: Allow LPM map access from sleepable BPF programs<br /> <br /> trie_lookup_elem() annotates its rcu_dereference_check() walks with<br /> only rcu_read_lock_bh_held(). Because rcu_dereference_check(p, c)<br /> resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and<br /> classic RCU readers but fails for sleepable BPF programs, which enter<br /> via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace().<br /> <br /> trie_update_elem() and trie_delete_elem() have the same problem in a<br /> different form: they walk the trie with plain rcu_dereference(), which<br /> asserts rcu_read_lock_held() unconditionally. Both are reachable from<br /> sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem<br /> helpers, and from the syscall path under classic rcu_read_lock(). In<br /> the writer paths the trie is actually protected by trie-&gt;lock (an<br /> rqspinlock taken across the walk); we never relied on the RCU read-side<br /> lock to keep nodes alive there.<br /> <br /> A sleepable LSM hook that ends up touching an LPM trie therefore<br /> triggers lockdep on debug kernels:<br /> <br /> =============================<br /> WARNING: suspicious RCU usage<br /> 7.1.0-... Tainted: G E<br /> -----------------------------<br /> kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage!<br /> 1 lock held by net_tests/540:<br /> #0: (rcu_tasks_trace_srcu_struct){....}-{0:0},<br /> at: __bpf_prog_enter_sleepable+0x26/0x280<br /> Call Trace:<br /> dump_stack_lvl<br /> lockdep_rcu_suspicious<br /> trie_lookup_elem<br /> bpf_prog_..._enforce_security_socket_connect<br /> bpf_trampoline_...<br /> security_socket_connect<br /> __sys_connect<br /> do_syscall_64<br /> <br /> This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize<br /> against the trie&amp;#39;s reclaim path -- but it spams the console once per<br /> distinct callsite on every debug kernel running a sleepable BPF LSM<br /> that touches an LPM trie, which is increasingly common.<br /> <br /> For the lookup path, switch the rcu_dereference_check() annotation<br /> from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all<br /> three contexts (classic, BH, Tasks Trace). Other map types already<br /> follow this convention.<br /> <br /> For trie_update_elem() and trie_delete_elem(), annotate the walks as<br /> rcu_dereference_protected(*p, 1) -- matching trie_free() in the same<br /> file -- since trie-&gt;lock is held across the walk. rqspinlock has no<br /> lockdep_map, so the predicate degenerates to &amp;#39;1&amp;#39; rather than<br /> lockdep_is_held(&amp;trie-&gt;lock); the protection is real but not<br /> machine-verifiable. trie_get_next_key() also uses bare<br /> rcu_dereference() but is reachable only from the BPF syscall, which<br /> holds classic rcu_read_lock() before dispatching, so it is left<br /> untouched.

Impact