bpf: Support <8-byte scalar spill and refill
Subsystems (5)
v2 cover bpf: Support <8-byte scalar spill and refill
The verifier currently does not save the reg state when spilling <8byte bounded scalar to the stack. The bpf program will be incorrectly rejected when this scalar is refilled to the reg and then used to offset into a packet header. The later patch has a simplified bpf prog from a real use case to demonstrate this case. The current work around is to reparse the packet again such that this offset scalar is close to where the packet data will be accessed to avoid the spill. Thus, the header is parsed twice.
The llvm patch [1] will align the <8bytes spill to the 8-byte stack address. This set is to make the necessary changes in verifier to support <8byte scalar spill and refill.
[1] https://reviews.llvm.org/D109073
v2:
- Changed the xdpwall selftest in patch 3 to trigger a u32 spill at a non 8-byte aligned stack address. The v1 has simplified the real example too much such that it only triggers a u32 spill but does not spill at a non 8-byte aligned stack address.
- Changed README.rst in patch 3 to explain the llvm dependency for the xdpwall test.
Martin KaFai Lau (4):
bpf: Check the other end of slot_type for STACK_SPILL
bpf: Support <8-byte scalar spill and refill
bpf: selftest: A bpf prog that has a 32bit scalar spill
bpf: selftest: Add verifier tests for <8-byte scalar spill and refill
kernel/bpf/verifier.c | 97 +++-- tools/testing/selftests/bpf/README.rst | 13 + .../selftests/bpf/prog_tests/xdpwall.c | 15 + tools/testing/selftests/bpf/progs/xdpwall.c | 365 ++++++++++++++++++ .../selftests/bpf/verifier/spill_fill.c | 161 ++++++++ 5 files changed, 625 insertions(+), 26 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/xdpwall.c create mode 100644 tools/testing/selftests/bpf/progs/xdpwall.c
Signature · 1 line
2.30.2
PATCH v2 1/4 bpf: Check the other end of slot_type for STACK_SPILL
Every 8 bytes of the stack is tracked by a bpf_stack_state. Within each bpf_stack_state, there is a 'u8 slot_type[8]' to track the type of each byte. Verifier tests slot_type[0] == STACK_SPILL to decide if the spilled reg state is saved. Verifier currently only saves the reg state if the whole 8 bytes are spilled to the stack, so checking the slot_type[7] is the same as checking slot_type[0].
The later patch will allow verifier to save the bounded scalar reg also for <8 bytes spill. There is a llvm patch [1] to ensure the <8 bytes spill will be 8-byte aligned, so checking slot_type[7] instead of slot_type[0] is required.
While at it, this patch refactors the slot_type[0] == STACK_SPILL test into a new function is_spilled_reg() and change the slot_type[0] check to slot_type[7] check in there also.
[1] https://reviews.llvm.org/D109073
--- kernel/bpf/verifier.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-)
kernel/bpf/verifier.c +19 -11
@@ -612,6 +612,14 @@ static const char *kernel_type_name(const struct btf* btf, u32 id)return btf_name_by_offset (btf,btf_type_by_id (btf, id)->name_off); } +/* The reg state of a pointer or a bounded scalar was saved when +* it was spilled to the stack. +*/ +static bool is_spilled_reg (const struct bpf_stack_state *stack) +{ +return stack->slot_type[BPF_REG_SIZE -1 ] ==STACK_SPILL ; +} +static void print_verifier_state (struct bpf_verifier_env *env,const struct bpf_func_state *state) {
@@ -717,7 +725,7 @@ static void print_verifier_state(struct bpf_verifier_env *env,continue ;verbose (env," fp%d" , (-i -1 ) *BPF_REG_SIZE );print_liveness (env, state->stack[i].spilled_ptr.live); -if (state->stack[i].slot_type[0 ] ==STACK_SPILL ) { +if (is_spilled_reg (&state->stack[i])) { reg = &state->stack[i].spilled_ptr; t = reg->type;verbose (env,"=%s" , reg_type_str[t]);
@@ -2373,7 +2381,7 @@ static void mark_all_scalars_precise(struct bpf_verifier_env *env, reg->precise = true; }for (j =0 ; j < func->allocated_stack /BPF_REG_SIZE ; j++) { -if (func->stack[j].slot_type[0 ] !=STACK_SPILL ) +if (!is_spilled_reg (&func->stack[j]))continue ; reg = &func->stack[j].spilled_ptr;if (reg->type !=SCALAR_VALUE )
@@ -2415,7 +2423,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno, }while (spi >=0 ) { -if (func->stack[spi].slot_type[0 ] !=STACK_SPILL ) { +if (!is_spilled_reg (&func->stack[spi])) { stack_mask =0 ;break ; }
@@ -2514,7 +2522,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno,return 0 ; } -if (func->stack[i].slot_type[0 ] !=STACK_SPILL ) { +if (!is_spilled_reg (&func->stack[i])) { stack_mask &= ~(1ull << i);continue ; }
@@ -2713,7 +2721,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,/* regular write of data into stack destroys any spilled ptr */ state->stack[spi].spilled_ptr.type =NOT_INIT ;/* Mark slots as STACK_MISC if they belonged to spilled ptr. */ -if (state->stack[spi].slot_type[0 ] ==STACK_SPILL ) +if (is_spilled_reg (&state->stack[spi]))for (i =0 ; i <BPF_REG_SIZE ; i++) state->stack[spi].slot_type[i] =STACK_MISC ;
@@ -2923,7 +2931,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, stype = reg_state->stack[spi].slot_type; reg = ®_state->stack[spi].spilled_ptr; -if (stype[0 ] ==STACK_SPILL ) { +if (is_spilled_reg (®_state->stack[spi])) {if (size !=BPF_REG_SIZE ) {if (reg->type !=SCALAR_VALUE ) {verbose_linfo (env, env->insn_idx,"; " );
@@ -4514,11 +4522,11 @@ static int check_stack_range_initialized( goto mark; } -if (state->stack[spi].slot_type[0 ] ==STACK_SPILL && +if (is_spilled_reg (&state->stack[spi]) && state->stack[spi].spilled_ptr.type ==PTR_TO_BTF_ID ) goto mark; -if (state->stack[spi].slot_type[0 ] ==STACK_SPILL && +if (is_spilled_reg (&state->stack[spi]) && (state->stack[spi].spilled_ptr.type ==SCALAR_VALUE || env->allow_ptr_leaks)) {if (clobber) {
@@ -10356,9 +10364,9 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old,* return false to continue verification of this path */ return false; -if (i %BPF_REG_SIZE ) +if (i %BPF_REG_SIZE !=BPF_REG_SIZE -1 )continue ; -if (old->stack[spi].slot_type[0 ] !=STACK_SPILL ) +if (!is_spilled_reg (&old->stack[spi]))continue ;if (!regsafe (env, &old->stack[spi].spilled_ptr, &cur->stack[spi].spilled_ptr, idmap))
@@ -10565,7 +10573,7 @@ static int propagate_precision(struct bpf_verifier_env *env, }for (i =0 ; i < state->allocated_stack /BPF_REG_SIZE ; i++) { -if (state->stack[i].slot_type[0 ] !=STACK_SPILL ) +if (!is_spilled_reg (&state->stack[i]))continue ; state_reg = &state->stack[i].spilled_ptr; if (state_reg->type != SCALAR_VALUE ||
Signature · 1 line
2.30.2
PATCH v2 2/4 bpf: Support <8-byte scalar spill and refill
In reply to Martin KaFai LauThe verifier currently does not save the reg state when spilling <8byte bounded scalar to the stack. The bpf program will be incorrectly rejected when this scalar is refilled to the reg and then used to offset into a packet header. The later patch has a simplified bpf prog from a real use case to demonstrate this case. The current work around is to reparse the packet again such that this offset scalar is close to where the packet data will be accessed to avoid the spill. Thus, the header is parsed twice.
The llvm patch [1] will align the <8bytes spill to the 8-byte stack address. This can simplify the verifier support by avoiding to store multiple reg states for each 8 byte stack slot.
This patch changes the verifier to save the reg state when spilling <8bytes scalar to the stack. This reg state saving is limited to spill aligned to the 8-byte stack address. The current refill logic has already called coerce_reg_to_size(), so coerce_reg_to_size() is not called on state->stack[spi].spilled_ptr during spill.
When refilling in check_stack_read_fixed_off(), it checks the refill size is the same as the number of bytes marked with STACK_SPILL before restoring the reg state. When restoring the reg state to state->regs[dst_regno], it needs
to avoid the state->regs[dst_regno].subreg_def being over written because it has been marked by the check_reg_arg() earlier [check_mem_access() is called after check_reg_arg() in do_check()]. Reordering check_mem_access() and check_reg_arg() will need a lot of changes in test_verifier's tests because of the difference in verifier's error message. Thus, the patch here is to save the state->regs[dst_regno].subreg_def first in check_stack_read_fixed_off().
There are cases that the verifier needs to scrub the spilled slot from STACK_SPILL to STACK_MISC. After this patch the spill is not always in 8 bytes now, so it can no longer assume the other 7 bytes are always marked as STACK_SPILL. In particular, the scrub needs to avoid marking an uninitialized byte from STACK_INVALID to STACK_MISC. Otherwise, the verifier will incorrectly accept bpf program reading uninitialized bytes from the stack. A new helper scrub_spilled_slot() is created for this purpose.
[1]: https://reviews.llvm.org/D109073
--- kernel/bpf/verifier.c | 67 +++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-)
kernel/bpf/verifier.c +52 -15
@@ -620,6 +620,12 @@ static bool is_spilled_reg(const struct bpf_stack_state *stack)return stack->slot_type[BPF_REG_SIZE -1 ] ==STACK_SPILL ; } +static void scrub_spilled_slot (u8 *stype) +{ +if (*stype !=STACK_INVALID ) + *stype =STACK_MISC ; +} +static void print_verifier_state (struct bpf_verifier_env *env,const struct bpf_func_state *state) {
@@ -2634,15 +2640,21 @@ static bool __is_pointer_value(bool allow_ptr_leaks, }static void save_register_state (struct bpf_func_state *state, -int spi,struct bpf_reg_state *reg) +int spi,struct bpf_reg_state *reg, +int size) {int i; state->stack[spi].spilled_ptr = *reg; - state->stack[spi].spilled_ptr.live |=REG_LIVE_WRITTEN ; +if (size ==BPF_REG_SIZE ) + state->stack[spi].spilled_ptr.live |=REG_LIVE_WRITTEN ; -for (i =0 ; i <BPF_REG_SIZE ; i++) - state->stack[spi].slot_type[i] =STACK_SPILL ; +for (i =BPF_REG_SIZE ; i >BPF_REG_SIZE - size; i--) + state->stack[spi].slot_type[i -1 ] =STACK_SPILL ; + +/* size < 8 bytes spill */ +for (; i; i--) +scrub_spilled_slot (&state->stack[spi].slot_type[i -1 ]); }/* check_stack_{read,write}_fixed_off functions track spill/fill of registers,
@@ -2689,7 +2701,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, env->insn_aux_data[insn_idx].sanitize_stack_spill = true; } -if (reg && size ==BPF_REG_SIZE &®ister_is_bounded (reg) && +if (reg && !(off %BPF_REG_SIZE ) &®ister_is_bounded (reg) && !register_is_null (reg) && env->bpf_capable) {if (dst_reg !=BPF_REG_FP ) {/* The backtracking logic can only recognize explicit
@@ -2702,7 +2714,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,if (err)return err; } -save_register_state (state, spi, reg); +save_register_state (state, spi, reg, size); }else if (reg &&is_spillable_regtype (reg->type)) {/* register containing pointer is being spilled into stack */ if (size !=BPF_REG_SIZE ) {
@@ -2714,7 +2726,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,verbose (env,"cannot spill pointers to stack into stack frame of the caller\n" );return -EINVAL ; } -save_register_state (state, spi, reg); +save_register_state (state, spi, reg, size); }else {u8 type =STACK_MISC ;
@@ -2723,7 +2735,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,/* Mark slots as STACK_MISC if they belonged to spilled ptr. */ if (is_spilled_reg (&state->stack[spi]))for (i =0 ; i <BPF_REG_SIZE ; i++) - state->stack[spi].slot_type[i] =STACK_MISC ; +scrub_spilled_slot (&state->stack[spi].slot_type[i]);/* only mark the slot as written if all 8 bytes were written * otherwise read propagation may incorrectly stop too soon
@@ -2926,23 +2938,50 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,struct bpf_func_state *state = vstate->frame[vstate->curframe];int i, slot = -off -1 , spi = slot /BPF_REG_SIZE ;struct bpf_reg_state *reg; -u8 *stype; +u8 *stype, type; stype = reg_state->stack[spi].slot_type; reg = ®_state->stack[spi].spilled_ptr;if (is_spilled_reg (®_state->stack[spi])) {if (size !=BPF_REG_SIZE ) { +u8 scalar_size =0 ; +if (reg->type !=SCALAR_VALUE ) {verbose_linfo (env, env->insn_idx,"; " );verbose (env,"invalid size of register fill\n" );return -EACCES ; } -if (dst_regno >=0 ) { + +mark_reg_read (env, reg, reg->parent,REG_LIVE_READ64 ); +if (dst_regno <0 ) +return 0 ; + +for (i =BPF_REG_SIZE ; i >0 && stype[i -1 ] ==STACK_SPILL ; i--) + scalar_size++; + +if (!(off %BPF_REG_SIZE ) && size == scalar_size) { +/* The earlier check_reg_arg() has decided the +* subreg_def for this insn. Save it first. +*/ +s32 subreg_def = state->regs[dst_regno].subreg_def; + + state->regs[dst_regno] = *reg; + state->regs[dst_regno].subreg_def = subreg_def; + }else { +for (i =0 ; i < size; i++) { + type = stype[(slot - i) %BPF_REG_SIZE ]; +if (type ==STACK_SPILL ) +continue ; +if (type ==STACK_MISC ) +continue ; +verbose (env,"invalid read from stack off %d+%d size %d\n" , + off, i, size); +return -EACCES ; + }mark_reg_unknown (env, state->regs, dst_regno); - state->regs[dst_regno].live |=REG_LIVE_WRITTEN ; } -mark_reg_read (env, reg, reg->parent,REG_LIVE_READ64 ); + state->regs[dst_regno].live |=REG_LIVE_WRITTEN ;return 0 ; }for (i =1 ; i <BPF_REG_SIZE ; i++) {
@@ -2973,8 +3012,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, }mark_reg_read (env, reg, reg->parent,REG_LIVE_READ64 ); }else { -u8 type; -for (i =0 ; i < size; i++) { type = stype[(slot - i) %BPF_REG_SIZE ];if (type ==STACK_MISC )
@@ -4532,7 +4569,7 @@ static int check_stack_range_initialized(if (clobber) {__mark_reg_unknown (env, &state->stack[spi].spilled_ptr);for (j =0 ; j <BPF_REG_SIZE ; j++) - state->stack[spi].slot_type[j] =STACK_MISC ; +scrub_spilled_slot (&state->stack[spi].slot_type[j]); } goto mark; }
Signature · 1 line
2.30.2
PATCH v2 3/4 bpf: selftest: A bpf prog that has a 32bit scalar spill
In reply to Martin KaFai LauIt is a simplified example that can trigger a 32bit scalar spill. The const scalar is refilled and added to a skb->data later. Since the reg state of the 32bit scalar spill is not saved now, adding the refilled reg to skb->data and then comparing it with skb->data_end cannot verify the skb->data access.
With the earlier verifier patch and the llvm patch [1]. The verifier can correctly verify the bpf prog.
Here is the snippet of the verifier log that leads to verifier conclusion
that the packet data is unsafe to read. The log is from the kerne
without the previous verifier patch to save the <8-byte scalar spill.
67: R0=inv1 R1=inv17 R2=invP2 R3=inv1 R4=pkt(id=0,off=68,r=102,imm=0) R5=inv102 R6=pkt(id=0,off=62,r=102,imm=0) R7=pkt(id=0,off=0,r=102,imm=0) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0
67: (63) *(u32 *)(r10 -12) = r5
68: R0=inv1 R1=inv17 R2=invP2 R3=inv1 R4=pkt(id=0,off=68,r=102,imm=0) R5=inv102 R6=pkt(id=0,off=62,r=102,imm=0) R7=pkt(id=0,off=0,r=102,imm=0) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0 fp-16=mmmm????
...
101: R0_w=map_value_or_null(id=2,off=0,ks=16,vs=1,imm=0) R6_w=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=0,off=0,r=102,imm=0) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0 fp-16=mmmmmmmm
101: (61) r1 = *(u32 *)(r10 -12)
102: R0_w=map_value_or_null(id=2,off=0,ks=16,vs=1,imm=0) R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6_w=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=0,off=0,r=102,imm=0) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0 fp-16=mmmmmmmm 102: (bc) w1 = w1
103: R0_w=map_value_or_null(id=2,off=0,ks=16,vs=1,imm=0) R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6_w=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=0,off=0,r=102,imm=0) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0 fp-16=mmmmmmmm 103: (0f) r7 += r1last_idx 103 first_idx 67
regs=2 stack=0 before 102: (bc) w1 = w1
regs=2 stack=0 before 101: (61) r1 = *(u32 *)(r10 -12)
104: R0_w=map_value_or_null(id=2,off=0,ks=16,vs=1,imm=0) R1_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6_w=pkt(id=0,off=70,r=102,imm=0) R7_w=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9=inv17 R10=fp0 fp-16=mmmmmmmm
...
127: R0_w=inv1 R1=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9_w=invP17 R10=fp0 fp-16=mmmmmmmm 127: (bf) r1 = r7
128: R0_w=inv1 R1_w=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9_w=invP17 R10=fp0 fp-16=mmmmmmmm 128: (07) r1 += 8
129: R0_w=inv1 R1_w=pkt(id=3,off=8,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9_w=invP17 R10=fp0 fp-16=mmmmmmmm 129: (b4) w0 = 1
130: R0=inv1 R1=pkt(id=3,off=8,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9=invP17 R10=fp0 fp-16=mmmmmmmm 130: (2d) if r1 > r8 goto pc-66
R0=inv1 R1=pkt(id=3,off=8,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9=invP17 R10=fp0 fp-16=mmmmmmmm
131: R0=inv1 R1=pkt(id=3,off=8,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R6=pkt(id=0,off=70,r=102,imm=0) R7=pkt(id=3,off=0,r=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R8=pkt_end(id=0,off=0,imm=0) R9=invP17 R10=fp0 fp-16=mmmmmmmm 131: (69) r6 = *(u16 *)(r7 +0)
invalid access to packet, off=0 size=2, R7(id=3,off=0,r=0)
R7 offset is outside of the packet
[1]: https://reviews.llvm.org/D109073
--- tools/testing/selftests/bpf/README.rst | 13 + .../selftests/bpf/prog_tests/xdpwall.c | 15 + tools/testing/selftests/bpf/progs/xdpwall.c | 365 ++++++++++++++++++ 3 files changed, 393 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/xdpwall.c create mode 100644 tools/testing/selftests/bpf/progs/xdpwall.c
tools/testing/selftests/bpf/README.rst +13 -0
@@ -242,3 +242,16 @@ To fix this issue, user newer libbpf. .. Links .. _clang reloc patch: https://reviews.llvm.org/D102712 .. _kernel llvm reloc: /Documentation/bpf/llvm_reloc.rst + +Clang dependencies for the u32 spill test (xdpwall) +=================================================== +The xdpwall selftest requires a change in `Clang 14`__. + +Without it, the xdpwall selftest will fail and the error message +from running test_progs will look like: + +.. code-block:: console + + test_xdpwall:FAIL:Does LLVM have https://reviews.llvm.org/D109073? unexpected error: -4007 + +__ https://reviews.llvm.org/D109073
tools/testing/selftests/bpf/prog_tests/xdpwall.c new file +15 -0
@@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2021 Facebook */ + +#include "test_progs.h" +#include "xdpwall.skel.h" + +void test_xdpwall (void ) +{ +struct xdpwall *skel; + + skel =xdpwall__open_and_load (); +ASSERT_OK_PTR (skel,"Does LLMV have https://reviews.llvm.org/D109073?" ); + +xdpwall__destroy (skel); +}
tools/testing/selftests/bpf/progs/xdpwall.c new file +365 -0
365 lines not on this page · raw message
Signature · 1 line
2.30.2
PATCH v2 4/4 bpf: selftest: Add verifier tests for <8-byte scalar spill and refill
In reply to Martin KaFai LauThis patch adds a few verifier tests for <8-byte spill and refill.
--- .../selftests/bpf/verifier/spill_fill.c | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+)
tools/testing/selftests/bpf/verifier/spill_fill.c +161 -0
@@ -104,3 +104,164 @@ .result = ACCEPT, .retval = POINTER_VALUE, }, +{ + "Spill and refill a u32 const scalar. Offset to skb->data", + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_2 ,BPF_REG_1 , + offsetof(struct __sk_buff , data)), +BPF_LDX_MEM (BPF_W ,BPF_REG_3 ,BPF_REG_1 , + offsetof(struct __sk_buff , data_end)), +/* r4 = 20 */ +BPF_MOV32_IMM (BPF_REG_4 ,20 ), +/* *(u32 *)(r10 -8) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* r4 = *(u32 *)(r10 -8) */ +BPF_LDX_MEM (BPF_W ,BPF_REG_4 ,BPF_REG_10 ,-8 ), +/* r0 = r2 */ +BPF_MOV64_REG (BPF_REG_0 ,BPF_REG_2 ), +/* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv20 */ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_4 ), +/* if (r0 > r3) R0=pkt,off=20 R2=pkt R3=pkt_end R4=inv20 */ +BPF_JMP_REG (BPF_JGT ,BPF_REG_0 ,BPF_REG_3 ,1 ), +/* r0 = *(u32 *)r2 R0=pkt,off=20,r=20 R2=pkt,r=20 R3=pkt_end R4=inv20 */ +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_2 ,0 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =ACCEPT , + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, +{ +"Spill a u32 const, refill from another half of the uninit u32 from the stack" , + .insns = { +/* r4 = 20 */ +BPF_MOV32_IMM (BPF_REG_4 ,20 ), +/* *(u32 *)(r10 -8) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* r4 = *(u32 *)(r10 -4) fp-8=????rrrr*/ +BPF_LDX_MEM (BPF_W ,BPF_REG_4 ,BPF_REG_10 ,-4 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =REJECT , + .errstr = "invalid read from stack off -4+0 size 4", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, +{ +"Spill a u32 const scalar. Refill as u16. Offset to skb->data" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_2 ,BPF_REG_1 , + offsetof(struct __sk_buff , data)), +BPF_LDX_MEM (BPF_W ,BPF_REG_3 ,BPF_REG_1 , + offsetof(struct __sk_buff , data_end)), +/* r4 = 20 */ +BPF_MOV32_IMM (BPF_REG_4 ,20 ), +/* *(u32 *)(r10 -8) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* r4 = *(u16 *)(r10 -8) */ +BPF_LDX_MEM (BPF_H ,BPF_REG_4 ,BPF_REG_10 ,-8 ), +/* r0 = r2 */ +BPF_MOV64_REG (BPF_REG_0 ,BPF_REG_2 ), +/* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_4 ), +/* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ +BPF_JMP_REG (BPF_JGT ,BPF_REG_0 ,BPF_REG_3 ,1 ), +/* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_2 ,0 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =REJECT , + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, +{ +"Spill a u32 const scalar. Refill as u16 from fp-6. Offset to skb->data" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_2 ,BPF_REG_1 , + offsetof(struct __sk_buff , data)), +BPF_LDX_MEM (BPF_W ,BPF_REG_3 ,BPF_REG_1 , + offsetof(struct __sk_buff , data_end)), +/* r4 = 20 */ +BPF_MOV32_IMM (BPF_REG_4 ,20 ), +/* *(u32 *)(r10 -8) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* r4 = *(u16 *)(r10 -6) */ +BPF_LDX_MEM (BPF_H ,BPF_REG_4 ,BPF_REG_10 ,-6 ), +/* r0 = r2 */ +BPF_MOV64_REG (BPF_REG_0 ,BPF_REG_2 ), +/* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=65535 */ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_4 ), +/* if (r0 > r3) R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv,umax=65535 */ +BPF_JMP_REG (BPF_JGT ,BPF_REG_0 ,BPF_REG_3 ,1 ), +/* r0 = *(u32 *)r2 R0=pkt,umax=65535 R2=pkt R3=pkt_end R4=inv20 */ +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_2 ,0 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =REJECT , + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, +{ +"Spill and refill a u32 const scalar at non 8byte aligned stack addr. Offset to skb->data" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_2 ,BPF_REG_1 , + offsetof(struct __sk_buff , data)), +BPF_LDX_MEM (BPF_W ,BPF_REG_3 ,BPF_REG_1 , + offsetof(struct __sk_buff , data_end)), +/* r4 = 20 */ +BPF_MOV32_IMM (BPF_REG_4 ,20 ), +/* *(u32 *)(r10 -8) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* *(u32 *)(r10 -4) = r4 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-4 ), +/* r4 = *(u32 *)(r10 -4), */ +BPF_LDX_MEM (BPF_W ,BPF_REG_4 ,BPF_REG_10 ,-4 ), +/* r0 = r2 */ +BPF_MOV64_REG (BPF_REG_0 ,BPF_REG_2 ), +/* r0 += r4 R0=pkt R2=pkt R3=pkt_end R4=inv,umax=U32_MAX */ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_0 ,BPF_REG_4 ), +/* if (r0 > r3) R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ +BPF_JMP_REG (BPF_JGT ,BPF_REG_0 ,BPF_REG_3 ,1 ), +/* r0 = *(u32 *)r2 R0=pkt,umax=U32_MAX R2=pkt R3=pkt_end R4=inv */ +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_2 ,0 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =REJECT , + .errstr = "invalid access to packet", + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +}, +{ +"Spill and refill a umax=40 bounded scalar. Offset to skb->data" , + .insns = { +BPF_LDX_MEM (BPF_W ,BPF_REG_2 ,BPF_REG_1 , + offsetof(struct __sk_buff , data)), +BPF_LDX_MEM (BPF_W ,BPF_REG_3 ,BPF_REG_1 , + offsetof(struct __sk_buff , data_end)), +BPF_LDX_MEM (BPF_DW ,BPF_REG_4 ,BPF_REG_1 , + offsetof(struct __sk_buff , tstamp)), +BPF_JMP_IMM (BPF_JLE ,BPF_REG_4 ,40 ,2 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), +BPF_EXIT_INSN (), +/* *(u32 *)(r10 -8) = r4 R4=inv,umax=40 */ +BPF_STX_MEM (BPF_W ,BPF_REG_10 ,BPF_REG_4 ,-8 ), +/* r4 = (*u32 *)(r10 - 8) */ +BPF_LDX_MEM (BPF_W ,BPF_REG_4 ,BPF_REG_10 ,-8 ), +/* r2 += r4 R2=pkt R4=inv,umax=40 */ +BPF_ALU64_REG (BPF_ADD ,BPF_REG_2 ,BPF_REG_4 ), +/* r0 = r2 R2=pkt,umax=40 R4=inv,umax=40 */ +BPF_MOV64_REG (BPF_REG_0 ,BPF_REG_2 ), +/* r2 += 20 R0=pkt,umax=40 R2=pkt,umax=40 */ +BPF_ALU64_IMM (BPF_ADD ,BPF_REG_2 ,20 ), +/* if (r2 > r3) R0=pkt,umax=40 R2=pkt,off=20,umax=40 */ +BPF_JMP_REG (BPF_JGT ,BPF_REG_2 ,BPF_REG_3 ,1 ), +/* r0 = *(u32 *)r0 R0=pkt,r=20,umax=40 R2=pkt,off=20,r=20,umax=40 */ +BPF_LDX_MEM (BPF_W ,BPF_REG_0 ,BPF_REG_0 ,0 ), +BPF_MOV64_IMM (BPF_REG_0 ,0 ), + BPF_EXIT_INSN(), + }, + .result =ACCEPT , + .prog_type = BPF_PROG_TYPE_SCHED_CLS, +},
Signature · 1 line
2.30.2