{"id":"CVE-2026-90317","state":"PUBLISHED","title":"bpf: Invalidate RCU pointers after final spin unlock","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nbpf: Invalidate RCU pointers after final spin unlock\n\nIn a sleepable BPF program, a spin lock can provide the only RCU protection\nfor a kptr. The final bpf_spin_unlock() ends that protection, but the\nverifier leaves the pointer valid. Another CPU can then free the object\nbefore the pointer is used. A capability-limited runtime PoC triggered a\ntask_struct use-after-free in __bpf_get_task_stack().\n\nRecord whether the program is in an RCU-protected context before releasing\nthe lock. Invalidate RCU-protected pointers only when the unlock leaves the\nfinal such context. This preserves valid pointers in non-sleepable programs\nand inside an explicit RCU read-side section.","publishedAt":"2026-09-17T16:08:35.537Z","updatedAt":"2026-10-19T09:01:33.647Z","severity":{"score":7.8,"label":"High","version":"3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","source":"Linux"},"assigner":"Linux","products":["Linux Linux"],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"f81972b235f3ebfa40bdce8bcfee3f242fb24065","versionType":"git"},{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"7ae71629357d0a6d0bdadb929c7b296d5b7e61c2","versionType":"git"},{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"180c7000712db77063b3a26f4c97e7dd9038f449","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["kernel/bpf/verifier.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.6","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.6","versionType":"semver"},{"version":"6.18.57","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.6","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["kernel/bpf/verifier.c"]}],"references":["https://git.kernel.org/stable/c/f81972b235f3ebfa40bdce8bcfee3f242fb24065","https://git.kernel.org/stable/c/7ae71629357d0a6d0bdadb929c7b296d5b7e61c2","https://git.kernel.org/stable/c/180c7000712db77063b3a26f4c97e7dd9038f449"],"weaknesses":[],"ingestedAt":"2026-10-29T20:13:34.582Z","source":{"repository":"CVEProject/cvelistV5","path":"cves/2026/90xxx/CVE-2026-90317.json","commit":"556c9d3a8cd17e218bb8e452ba7cd619a384ff47"},"raw":{"dataType":"CVE_RECORD","dataVersion":"5.2","cveMetadata":{"cveId":"CVE-2026-90317","state":"PUBLISHED","datePublished":"2026-09-17T16:08:35.537Z","dateUpdated":"2026-10-19T09:01:33.647Z","assignerShortName":"Linux","assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateReserved":"2026-09-11T19:38:34.801Z"},"containers":{"cna":{"title":"bpf: Invalidate RCU pointers after final spin unlock","descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nbpf: Invalidate RCU pointers after final spin unlock\n\nIn a sleepable BPF program, a spin lock can provide the only RCU protection\nfor a kptr. The final bpf_spin_unlock() ends that protection, but the\nverifier leaves the pointer valid. Another CPU can then free the object\nbefore the pointer is used. A capability-limited runtime PoC triggered a\ntask_struct use-after-free in __bpf_get_task_stack().\n\nRecord whether the program is in an RCU-protected context before releasing\nthe lock. Invalidate RCU-protected pointers only when the unlock leaves the\nfinal such context. This preserves valid pointers in non-sleepable programs\nand inside an explicit RCU read-side section."}],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"f81972b235f3ebfa40bdce8bcfee3f242fb24065","versionType":"git"},{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"7ae71629357d0a6d0bdadb929c7b296d5b7e61c2","versionType":"git"},{"version":"5861d1e8dbc4e1a03ebffb96ac041026cdd34c07","status":"affected","lessThan":"180c7000712db77063b3a26f4c97e7dd9038f449","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["kernel/bpf/verifier.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.6","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.6","versionType":"semver"},{"version":"6.18.57","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.6","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["kernel/bpf/verifier.c"]}],"references":[{"url":"https://git.kernel.org/stable/c/f81972b235f3ebfa40bdce8bcfee3f242fb24065"},{"url":"https://git.kernel.org/stable/c/7ae71629357d0a6d0bdadb929c7b296d5b7e61c2"},{"url":"https://git.kernel.org/stable/c/180c7000712db77063b3a26f4c97e7dd9038f449"}],"metrics":[{"cvssV3_1":{"version":"3.1","vectorString":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","baseScore":7.8,"baseSeverity":"HIGH"},"scenarios":[{"lang":"en","value":"AV:L - The attacker-controlled input is a sleepable BPF program loaded via bpf(BPF_PROG_LOAD) whose bytecode reaches process_spin_lock() in kernel/bpf/verifier.c, takes bpf_spin_lock, LDXes a map kptr, then bpf_spin_unlock. That program image arrives through the local bpf() syscall, not a network protocol message.\nAC:L - The attacker drives both sides: a sleepable fentry.s or BPF_PROG_TYPE_SYSCALL program loads a task kptr under bpf_spin_lock, unlocks, then uses it, while another thread bpf_kptr_xchg's the same slot to NULL so bpf_task_release_dtor/put_task_struct_rcu_user can call_rcu it. After unlock, preempt/IRQs are re-enabled so the grace period can finish; a PoC hit the UAF.\nPR:L - A sleepable BPF_PROG_TYPE_SYSCALL program is sufficient: bpf_prog_load() requires only bpf_token_capable(CAP_BPF) because is_perfmon_prog_type() is false for SYSCALL, and map_check_btf() needs CAP_BPF for BPF_SPIN_LOCK|BPF_KPTR fields. CAP_BPF is delegable via BPF tokens into a user namespace; the report used a capability-limited PoC, not init-namespace root.\nUI:N - The attacker loads the sleepable program, creates the lock+kptr ARRAY map, stores a task with bpf_task_from_pid/bpf_kptr_xchg, and runs it via bpf(BPF_PROG_TEST_RUN) (bpf_prog_test_run_syscall) or by calling getpgid after attaching fentry.s; no victim mount or file open is required.\nS:U - The use-after-free is of a host kernel task_struct consumed by bpf_task_acquire and __bpf_get_task_stack in the same kernel that accepted the program. That is local privilege escalation inside one kernel authority, not a VM, IOMMU, or guest-to-host crossing.\nC:H - After the final bpf_spin_unlock, process_spin_lock() leaves the map-loaded task kptr as MEM_RCU, so the program can LDX task_struct fields or call bpf_task_acquire (refcount_inc_not_zero on rcu_users) and __bpf_get_task_stack (try_get_task_stack/task_pt_regs) on a call_rcu-freed object. Spraying that slot yields an arbitrary kernel read.\nI:H - The same stale MEM_RCU task_struct pointer can be passed to bpf_task_acquire after bpf_task_release_dtor has dropped rcu_users and delayed_put_task_struct has freed it; a sprayed replacement becomes an owned kptr the attacker can feed to other kfuncs or store back with bpf_kptr_xchg, giving a write and control-flow primitive.\nA:H - Using the dangling task pointer in __bpf_get_task_stack (try_get_task_stack/task_pt_regs) or bpf_task_acquire after put_task_struct_rcu_user's call_rcu has run oopses or panics the kernel even without a full exploit."}]}],"providerMetadata":{"shortName":"Linux","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateUpdated":"2026-10-19T09:01:33.647Z"},"cpeApplicability":[{"nodes":[{"operator":"OR","negate":false,"cpeMatch":[{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.6","versionEndExcluding":"6.18.57"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.6","versionEndExcluding":"7.2.6"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.6","versionEndExcluding":"7.3"}]}]}],"x_generator":{"engine":"bippy-1.2.0"}}}}}