In the Linux kernel, the following vulnerability has been resolved:
bpf: Fix REG INVARIANTS VIOLATION on speculative pointer arithmetic
Take the following unprivileged program as an example:
r0 = bpf_map_lookup_elem(...) /* PTR_TO_MAP_VALUE, offset 0 */ ... 14: r0 += r1 /* r1 is a bounded scalar */ 15: r9 = r0
Loading it triggers a verifier warning from reg_bounds_sanity_check():
verifier bug: REG INVARIANTS VIOLATION (alu): const subreg tnum out of sync with range bounds r64={.base=0x0, .size=0x0} r32={.base=0x0, .size=0xffffffff} var_off=(0x0, 0x0)
What happens:
- Processing insn 14 (r0 += r1) in adjust_ptr_min_max_vals(), the new
- Because pointer registers do not track 32-bit subregister bounds,
- On the unprivileged path, sanitize_ptr_alu() is called and, via
- That snapshot is taken between step 2 and the final reg_bounds_sync():
var_off and the 32-bit range must always be consistent. There are two ways to keep the snapshot consistent:
- sync var_off and r32 before the snapshot so they match, or
- leave r32 at its original (already consistent) value and blank it
The whole point of sanitize_ptr_alu() is to insert a harmless masking sequence that keeps the access in bounds under speculation, so the state it snapshots should faithfully represent that. Take approach 2: move __mark_reg32_unbounded() to after sanitize_ptr_alu(), so the speculative snapshot keeps the pointer's original, consistent r32. The non-speculative path is unchanged: r32 is still blanked before the offset is applied and re-derived by reg_bounds_sync().