PT-2026-64573 · Linux · Linux
CVE-2026-64352
·
Published
2026-07-25
·
Updated
2026-07-25
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
In the Linux kernel, the following vulnerability has been resolved:
bpf: Allow LPM map access from sleepable BPF programs
trie lookup elem() annotates its rcu dereference check() walks with
only rcu read lock bh held(). Because rcu dereference check(p, c)
resolves to "c || rcu read lock held()", this passes for XDP/NAPI and
classic RCU readers but fails for sleepable BPF programs, which enter
via bpf prog enter sleepable() and hold only rcu read lock trace().
trie update elem() and trie delete elem() have the same problem in a
different form: they walk the trie with plain rcu dereference(), which
asserts rcu read lock held() unconditionally. Both are reachable from
sleepable BPF programs via the bpf map update elem / bpf map delete elem
helpers, and from the syscall path under classic rcu read lock(). In
the writer paths the trie is actually protected by trie->lock (an
rqspinlock taken across the walk); we never relied on the RCU read-side
lock to keep nodes alive there.
A sleepable LSM hook that ends up touching an LPM trie therefore
triggers lockdep on debug kernels:
============================= WARNING: suspicious RCU usage 7.1.0-... Tainted: G E
kernel/bpf/lpm trie.c:249 suspicious rcu dereference check() usage!
1 lock held by net tests/540:
#0: (rcu tasks trace srcu struct){....}-{0:0},
at: bpf prog enter sleepable+0x26/0x280
Call Trace:
dump stack lvl
lockdep rcu suspicious
trie lookup elem
bpf prog ... enforce security socket connect
bpf trampoline ...
security socket connect
sys connect
do syscall 64
This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize
against the trie's reclaim path -- but it spams the console once per
distinct callsite on every debug kernel running a sleepable BPF LSM
that touches an LPM trie, which is increasingly common.
For the lookup path, switch the rcu dereference check() annotation
from rcu read lock bh held() to bpf rcu lock held(), which accepts all
three contexts (classic, BH, Tasks Trace). Other map types already
follow this convention.
For trie update elem() and trie delete elem(), annotate the walks as
rcu dereference protected(*p, 1) -- matching trie free() in the same
file -- since trie->lock is held across the walk. rqspinlock has no
lockdep map, so the predicate degenerates to '1' rather than
lockdep is held(&trie->lock); the protection is real but not
machine-verifiable. trie get next key() also uses bare
rcu dereference() but is reachable only from the BPF syscall, which
holds classic rcu read lock() before dispatching, so it is left
untouched.
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Linux