diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-11-02 17:09:57 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-11-02 20:26:01 +0000 |
| commit | 51f6627d44f3b0e070f33187aa5192d6f8484be9 (patch) | |
| tree | 43826fc16c6b9d8f2ed640434a7bfa898bd59886 /scripts/basic/split-include.c | |
| parent | 8ed2506a7c2712142e4ee37bc82f018741cf4b9c (diff) | |
| download | linux-51f6627d44f3b0e070f33187aa5192d6f8484be9.tar.gz linux-51f6627d44f3b0e070f33187aa5192d6f8484be9.zip | |
bpf: Invalidate a dynptr slice when its stack buffer is written
bpf_dynptr_slice(&ptr, off, buffer, size) and bpf_dynptr_slice_rdwr()
return a pointer to the data of a dynptr. When the data is not contiguous,
for example in a non-linear skb, they copy it to 'buffer' and return
'buffer'. Usually the buffer is on the stack. For the verifier the result
is plain memory (PTR_TO_MEM), and an access through it is not an access to
the stack:
- A store makes wrong what the verifier tracks in the buffer: a known
scalar, a spilled pointer, a dynptr, an iterator, irq flags.
- A load gives the bytes of a spilled pointer or of a dynptr as a scalar.
- Both are possible after the frame of the buffer returned.
The accesses are safe while the buffer contains only unknown bytes, as it
does after the call. Thus keep this rule: the buffer contains no tracked
data while a slice that can be the buffer exists. The slice remembers the
frame, the offset and the size of its buffer. The verifier invalidates the
slice, read-only or not, at each event that breaks the rule:
event slices of the buffer
------------------------------------------------- --------------------
the prog stores to bytes of the buffer invalid
a dynptr, an iterator or irq flags are made there invalid
the frame of the buffer returns invalid
a helper or a kfunc fills the buffer no change
a store next to the buffer, a read of the buffer no change
Tracked data gets into a stack slot in check_stack_write(), also the bytes
of a new dynptr, iterator or irq flags. Thus one call there, and one call
in prepare_func_exit(), are sufficient. Each is one walk over the
registers, only in progs that have such a slice. Helpers and kfuncs leave
only unknown bytes: mark_raw_stack() goes around check_stack_write().
The range of the buffer is in the part of bpf_reg_state that the verifier
copies with the register and compares for pruning. A slice that is the
buffer of a second call passes its range on. Local, ringbuf and skb
metadata dynptrs never return the buffer: no range. A dynptr argument of a
global function counts as an skb dynptr.
The usual pattern only reads the buffer, and it continues to load:
if (p == buffer)
bpf_dynptr_write(&ptr, 0, buffer, sizeof(buffer), 0);
A prog that stores to the buffer and then uses the old slice is rejected:
"R7 invalid mem access 'scalar'". The diagnostics name the reason, and the
log shows the buffer of a slice: R7=mem(sz=8,buf=fp[0]-8+8).
Fixes: 66e3a13e7c2c ("bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261102170958.3586893-2-alexei.starovoitov@gmail.com
Diffstat (limited to 'scripts/basic/split-include.c')
0 files changed, 0 insertions, 0 deletions
