CVE-2026-45933
High · 7.8CVSS
7.8
EPSS
0.2%
Published
May 27, 2026
CISA due date
—
Cloudanix analysis
A bug in the Linux kernel's safety checker for certain low-level programs could let a local user bypass checks and run code that should have been blocked, risking crashes or unauthorized access.
- Exploitation
- Local Privilege
- Attacker needs
- Low Privilege
- Complexity
- Moderate
- Impact
- Full Compromise
- Component
- OS kernel
- Class
- Improper Input Validation
- Remediation
- Upgrade
- Effort
- Medium
- Patch risk
- Low
What it actually does
The flaw is in the BPF verifier's sync_linked_regs() function, which fails to preserve the original register ID when propagating bounds from a linked register that has an added constant offset; this incorrectly assigns a new ID via assign_scalar_id_before_mov(), breaking subsequent bound propagation between registers and allowing the verifier to accept unsafe states such as division by zero.
Why it matters to the business
Local users on affected Linux systems can craft BPF programs that evade safety checks, potentially leading to kernel crashes, denial of service, or local privilege escalation on systems that allow unprivileged BPF loading.
How an attack unfolds
An attacker compiles and loads a malicious BPF program that exercises register linking, offset addition, and conditional bounds; the verifier bug causes an ID to be reassigned, so later bounds updates are not propagated, allowing the program to reach an invalid operation (such as division by zero) that the verifier should have rejected.
How to fix it
Upgrade the Linux kernel to a version containing the fix that preserves the register ID inside sync_linked_regs(); rebuild and redeploy any custom BPF programs after the update; restrict unprivileged BPF loading via kernel configuration where feasible.
How to confirm the fix worked
Confirm the running kernel contains the upstream commit that adds ID preservation to sync_linked_regs(); run the selftest added in the same patch series to verify the div-by-zero case is now rejected; re-test any workloads that load untrusted BPF programs.
Typical attack path
- Obtain local access on a Linux system permitting BPF program loading
- Load a crafted BPF program containing register moves, additions, and conditional jumps that trigger sync_linked_regs()
- Exploit the ID reassignment to break register linking and bypass bound checks
- Reach and execute an unsafe instruction (for example, division by zero) inside the verifier-approved program
How to detect it
Search kernel logs for BPF verifier messages around register ID handling or unexpected bound propagation; look for BPF programs that reach division-by-zero or similar arithmetic errors after loading successfully; examine audit logs for unusual bpf() syscall patterns from non-root users.
What your security team should do
- Apply the kernel fix on all systems that permit unprivileged or low-privileged BPF loading
- Audit kernel configurations to disable unprivileged BPF where not required
- Monitor upstream stable kernel releases for backports of this verifier fix
- Review any custom or third-party BPF programs for reliance on the previously broken register-linking behavior
The record
In the Linux kernel, the following vulnerability has been resolved: bpf: Preserve id of register in sync_linked_regs() sync_linked_regs() copies the id of known_reg to reg when propagating bounds of known_reg to reg using the off of known_reg, but when known_reg was linked to reg like: known_reg = reg ; both known_reg and reg get same id known_reg += 4 ; known_reg gets off = 4, and its id gets BPF_ADD_CONST now when a call to sync_linked_regs() happens, let's say with the following: if known_reg >= 10 goto pc+2 known_reg's new bounds are propagated to reg but now reg gets BPF_ADD_CONST from the copy. This means if another link to reg is created like: another_reg = reg ; another_reg should get the id of reg but assign_scalar_id_before_mov() sees BPF_ADD_CONST on reg and assigns a new id to it. As reg has a new id now, known_reg's link to reg is broken. If we find new bounds for known_reg, they will not be propagated to reg. This can be seen in the selftest added in the next commit: 0: (85) call bpf_get_prandom_u32#7 ; R0=scalar() 1: (57) r0 &= 255 ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) 2: (bf) r1 = r0 ; R0=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R1=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) 3: (07) r1 += 4 ; R1=scalar(id=1+4,smin=umin=smin32=umin32=4,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff)) 4: (a5) if r1 < 0xa goto pc+4 ; R1=scalar(id=1+4,smin=umin=smin32=umin32=10,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff)) 5: (bf) r2 = r0 ; R0=scalar(id=2,smin=umin=smin32=umin32=6,smax=umax=smax32=umax32=255) R2=scalar(id=2,smin=umin=smin32=umin32=6,smax=umax=smax32=umax32=255) 6: (a5) if r1 < 0xe goto pc+2 ; R1=scalar(id=1+4,smin=umin=smin32=umin32=14,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff)) 7: (35) if r0 >= 0xa goto pc+1 ; R0=scalar(id=2,smin=umin=smin32=umin32=6,smax=umax=smax32=umax32=9,var_off=(0x0; 0xf)) 8: (37) r0 /= 0 div by zero When 4 is verified, r1's bounds are propagated to r0 but r0 also gets BPF_ADD_CONST (bug). When 5 is verified, r0 gets a new id (2) and its link with r1 is broken. After 6 we know r1 has bounds [14, 259] and therefore r0 should have bounds [10, 255], therefore the branch at 7 is always taken. But because r0's id was changed to 2, r1's new bounds are not propagated to r0. The verifier still thinks r0 has bounds [6, 255] before 7 and execution can reach div by zero. Fix this by preserving id in sync_linked_regs() like off and subreg_def.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|