VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45933

CVE-2026-45933

Description

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.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

In the Linux kernel, a BPF verifier bug in `sync_linked_regs()` can break register link tracking, leading to incorrect bounds propagation and potential security bypass.

Vulnerability

In the Linux kernel's BPF verifier, the function sync_linked_regs() propagates bounds from a known_reg to a linked register reg. However, when known_reg has been modified by an addition (e.g., known_reg += 4), its identifier is changed to BPF_ADD_CONST. The function incorrectly copies this modified identifier to reg, causing reg to lose its original link. This breaks subsequent link tracking, as demonstrated in the selftest described in the commit. Affected versions include all Linux kernel versions prior to the fix commit af9e89d8dd39530c8bd14c33ddf6b502df1071b6 [1].

Exploitation

An attacker with the ability to load BPF programs (typically requiring CAP_BPF or root privileges, or unprivileged BPF if enabled) can craft a BPF program that triggers the bug. By creating linked registers and performing arithmetic operations, the attacker can cause the verifier to lose track of register relationships. This allows the attacker to bypass bounds checks and potentially introduce unsafe operations that the verifier would normally reject.

Impact

Successful exploitation could allow a BPF program to bypass the verifier's safety checks, leading to out-of-bounds memory access or other violations. This could result in arbitrary kernel memory read or write, potentially leading to privilege escalation or information disclosure. The exact impact depends on the specific BPF program and kernel configuration.

Mitigation

The vulnerability is fixed in Linux kernel commit af9e89d8dd39530c8bd14c33ddf6b502df1071b6 [1]. Users should update to a kernel version that includes this commit. No workaround is available; the fix must be applied by updating the kernel. The commit is included in the stable kernel tree.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

8
af9e89d8dd39

bpf: Preserve id of register in sync_linked_regs()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitPuranjay MohanJan 15, 2026Fixed in 7.0via kernel-cna
1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 7a375f608263d9..9de0ec0c3ed998 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16871,6 +16871,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16878,10 +16879,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
58059335e465

bpf: Preserve id of register in sync_linked_regs()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitPuranjay MohanJan 15, 2026Fixed in 6.12.75via kernel-cna
1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 7b75a2dd8cb8f3..08cdf6ace02ac3 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -15478,6 +15478,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -15485,10 +15486,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
92a8cb1806ad

bpf: Preserve id of register in sync_linked_regs()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitPuranjay MohanJan 15, 2026Fixed in 6.18.14via kernel-cna
1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 89560e455ce7b2..14546d1bdb52c6 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16715,6 +16715,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16722,10 +16723,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
65d114b5270b

bpf: Preserve id of register in sync_linked_regs()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitPuranjay MohanJan 15, 2026Fixed in 6.19.4via kernel-cna
1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 646025bae96db1..8678ce5c97c5a3 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16827,6 +16827,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16834,10 +16835,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
58059335e465

bpf: Preserve id of register in sync_linked_regs()

1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 7b75a2dd8cb8f3..08cdf6ace02ac3 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -15478,6 +15478,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -15485,10 +15486,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
65d114b5270b

bpf: Preserve id of register in sync_linked_regs()

1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 646025bae96db1..8678ce5c97c5a3 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16827,6 +16827,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16834,10 +16835,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
92a8cb1806ad

bpf: Preserve id of register in sync_linked_regs()

1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 89560e455ce7b2..14546d1bdb52c6 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16715,6 +16715,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16722,10 +16723,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    
af9e89d8dd39

bpf: Preserve id of register in sync_linked_regs()

1 file changed · +3 2
  • kernel/bpf/verifier.c+3 2 modified
    diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
    index 7a375f608263d9..9de0ec0c3ed998 100644
    --- a/kernel/bpf/verifier.c
    +++ b/kernel/bpf/verifier.c
    @@ -16871,6 +16871,7 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     		} else {
     			s32 saved_subreg_def = reg->subreg_def;
     			s32 saved_off = reg->off;
    +			u32 saved_id = reg->id;
     
     			fake_reg.type = SCALAR_VALUE;
     			__mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off);
    @@ -16878,10 +16879,11 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
     			/* reg = known_reg; reg += delta */
     			copy_register_state(reg, known_reg);
     			/*
    -			 * Must preserve off, id and add_const flag,
    +			 * Must preserve off, id and subreg_def flag,
     			 * otherwise another sync_linked_regs() will be incorrect.
     			 */
     			reg->off = saved_off;
    +			reg->id = saved_id;
     			reg->subreg_def = saved_subreg_def;
     
     			scalar32_min_max_add(reg, &fake_reg);
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"sync_linked_regs() overwrites reg->id with known_reg->id (BPF_ADD_CONST) during bound propagation, breaking the id-based link between registers and preventing future bound propagation."

Attack vector

An attacker who can load a crafted BPF program into the kernel can trigger this bug. The program creates a linked register pair (e.g., `r1 = r0`), then adds a constant to one register (`r1 += 4`), which changes its id to `BPF_ADD_CONST`. When a conditional jump propagates bounds from `r1` back to `r0`, `sync_linked_regs()` overwrites `r0`'s id with `BPF_ADD_CONST`. A subsequent move (`r2 = r0`) then sees `BPF_ADD_CONST` on `r0` and assigns a new id to `r0`, breaking the link between `r0` and `r1`. The verifier then fails to propagate tightened bounds from `r1` to `r0`, allowing a division-by-zero to be reachable that the verifier would otherwise reject [patch_id=2661223].

Affected code

The bug is in the `sync_linked_regs()` function in `kernel/bpf/verifier.c` [patch_id=2661223]. When propagating bounds from `known_reg` to `reg`, the function calls `copy_register_state(reg, known_reg)` which overwrites `reg->id` with `known_reg->id` (which may be `BPF_ADD_CONST`). The original `reg->id` was not saved and restored, unlike `off` and `subreg_def` which were preserved.

What the fix does

The patch saves `reg->id` into a local `saved_id` before `copy_register_state()` overwrites it, then restores `reg->id = saved_id` after the copy [patch_id=2661223]. This ensures that when bounds are propagated from `known_reg` to `reg`, the original id of `reg` is preserved, maintaining the link between `reg` and any other registers that were previously assigned from it. The comment is also updated from "Must preserve off, id and add_const flag" to "Must preserve off, id and subreg_def flag" to reflect the corrected semantics.

Preconditions

  • authAttacker must be able to load a crafted BPF program (requires CAP_BPF or equivalent privileges)
  • inputThe BPF program must create linked scalar registers, add a constant to one, then perform a conditional jump and a subsequent move from the original register

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.