Fixes for stack with allow_ptr_leaks
Subsystems (4)
Kumar Kartikeya Dwivedi
maintainerv4 cover Fixes for stack with allow_ptr_leaks
Two fixes for usability/correctness gaps when interacting with the stack without CAP_PERFMON (i.e. with allow_ptr_leaks = false). See the commits for details. I've verified that the tests fail when run without the fixes.
Changelog:
----------
v3 -> v4
v3: https://lore.kernel.org/bpf/[email protected]
* Address Andrii's comments
* Fix bug paperered over by missing CAP_NET_ADMIN in verifier_mtu test
* Add warning when undefined CAP_ constant is specified, and fail test
* Reorder annotations to be more clear
* Verify that fixes fail without patches again
* Add Acked-by from Andrii
v2 -> v3
v2: https://lore.kernel.org/bpf/[email protected]
* Address comments from Eduard
* Fix comment for mark_stack_slot_misc
* We can simply always return early when stype == STACK_INVALID
* Drop allow_ptr_leaks conditionals
* Add Eduard's __caps_unpriv patch into the series
* Convert test_verifier_mtu to use it
* Move existing tests to __caps_unpriv annotation and verifier_spill_fill.c
* Add Acked-by from Eduard
v1 -> v2
v1: https://lore.kernel.org/bpf/[email protected]
* Fix CI errors in selftest by removing dependence on BPF_ST
Eduard Zingerman (1):
selftests/bpf: Introduce __caps_unpriv annotation for tests
Kumar Kartikeya Dwivedi (3):
bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc selftests/bpf: Add test for reading from STACK_INVALID slots selftests/bpf: Add test for narrow spill into 64-bit spilled scalar
Tao Lyu (1):
bpf: Fix narrow scalar spill onto 64-bit spilled scalar slots
kernel/bpf/verifier.c | 10 ++-- .../selftests/bpf/prog_tests/verifier.c | 19 +------- tools/testing/selftests/bpf/progs/bpf_misc.h | 12 +++++ .../selftests/bpf/progs/verifier_mtu.c | 4 +- .../selftests/bpf/progs/verifier_spill_fill.c | 35 ++++++++++++++ tools/testing/selftests/bpf/test_loader.c | 46 +++++++++++++++++++ 6 files changed, 104 insertions(+), 22 deletions(-)
base-commit: 5a6ea7022ff4d2a65ae328619c586d6a8909b48b
Signature · 1 line
2.43.5
Kumar Kartikeya Dwivedi
maintainerPATCH v4 1/5 bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc
Inside mark_stack_slot_misc, we should not upgrade STACK_INVALID to STACK_MISC when allow_ptr_leaks is false, since invalid contents shouldn't be read unless the program has the relevant capabilities. The relaxation only makes sense when env->allow_ptr_leaks is true.
However, such conversion in privileged mode becomes unnecessary, as invalid slots can be read without being upgraded to STACK_MISC.
Currently, the condition is inverted (i.e. checking for true instead of false), simply remove it to restore correct behavior.
--- kernel/bpf/verifier.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
kernel/bpf/verifier.c +6 -3
@@ -1202,14 +1202,17 @@ static bool is_spilled_scalar_reg64(const struct bpf_stack_state *stack)/* Mark stack slot as STACK_MISC, unless it is already STACK_INVALID, in which * case they are equivalent, or it's STACK_ZERO, in which case we preserve * more precise STACK_ZERO. -* Note, in uprivileged mode leaving STACK_INVALID is wrong, so we take -* env->allow_ptr_leaks into account and force STACK_MISC, if necessary. +* Regardless of allow_ptr_leaks setting (i.e., privileged or unprivileged +* mode), we won't promote STACK_INVALID to STACK_MISC. In privileged case it is +* unnecessary as both are considered equivalent when loading data and pruning, +* in case of unprivileged mode it will be incorrect to allow reads of invalid +* slots. */ static void mark_stack_slot_misc (struct bpf_verifier_env *env,u8 *stype) {if (*stype ==STACK_ZERO )return ; -if (env->allow_ptr_leaks && *stype ==STACK_INVALID ) +if (*stype ==STACK_INVALID )return ; *stype =STACK_MISC ; }
Signature · 1 line
2.43.5
Kumar Kartikeya Dwivedi
maintainerPATCH v4 2/5 bpf: Fix narrow scalar spill onto 64-bit spilled scalar slots
In reply to Kumar Kartikeya DwivediFrom: Tao Lyu <[email protected]>
When CAP_PERFMON and CAP_SYS_ADMIN (allow_ptr_leaks) are disabled, the verifier aims to reject partial overwrite on an 8-byte stack slot that contains a spilled pointer.
However, in such a scenario, it rejects all partial stack overwrites as long as the targeted stack slot is a spilled register, because it does not check if the stack slot is a spilled pointer.
Incomplete checks will result in the rejection of valid programs, which spill narrower scalar values onto scalar slots, as shown below.
0: R1=ctx() R10=fp0
; asm volatile ( @ repro.bpf.c:679
0: (7a) *(u64 *)(r10 -8) = 1 ; R10=fp0 fp-8_w=1
1: (62) *(u32 *)(r10 -8) = 1
attempt to corrupt spilled pointer on stack
processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0.
Fix this by expanding the check to not consider spilled scalar registers when rejecting the write into the stack.
Previous discussion on this patch is at link [0].
[0]: https://lore.kernel.org/bpf/[email protected]
--- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+)
kernel/bpf/verifier.c +1 -0
@@ -4703,6 +4703,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,*/ if (!env->allow_ptr_leaks &&is_spilled_reg (&state->stack[spi]) && + !is_spilled_scalar_reg (&state->stack[spi]) && size !=BPF_REG_SIZE ) {verbose (env,"attempt to corrupt spilled pointer on stack\n" );return -EACCES ;
Signature · 1 line
2.43.5
Kumar Kartikeya Dwivedi
maintainerPATCH v4 3/5 selftests/bpf: Introduce __caps_unpriv annotation for tests
In reply to Kumar Kartikeya DwivediFrom: Eduard Zingerman <[email protected]>
Add a __caps_unpriv annotation so that tests requiring specific capabilities while dropping the rest can conveniently specify them during selftest declaration instead of munging with capabilities at runtime from the testing binary.
While at it, let us convert test_verifier_mtu to use this new support instead.
Since we do not want to include linux/capability.h, we only defined the four main capabilities BPF subsystem deals with in bpf_misc.h for use in tests. If the user passes a CAP_SYS_NICE or anything else that's not defined in the header, capability parsing code will return a warning.
Also reject strtol returning 0. CAP_CHOWN = 0 but we'll never need to use it, and strtol doesn't errno on failed conversion. Fail the test in such a case.
The original diff for this idea is available at link [0].
[0]: https://lore.kernel.org/bpf/[email protected]
--- .../selftests/bpf/prog_tests/verifier.c | 19 +------- tools/testing/selftests/bpf/progs/bpf_misc.h | 12 +++++ .../selftests/bpf/progs/verifier_mtu.c | 4 +- tools/testing/selftests/bpf/test_loader.c | 46 +++++++++++++++++++ 4 files changed, 62 insertions(+), 19 deletions(-)
tools/testing/selftests/bpf/prog_tests/verifier.c +1 -18
@@ -225,24 +225,7 @@ void test_verifier_xdp(void) { RUN(verifier_xdp); }void test_verifier_xdp_direct_packet_access (void ) {RUN (verifier_xdp_direct_packet_access); }void test_verifier_bits_iter (void ) {RUN (verifier_bits_iter); }void test_verifier_lsm (void ) {RUN (verifier_lsm); } - -void test_verifier_mtu (void ) -{ -__u64 caps =0 ; -int ret; - -/* In case CAP_BPF and CAP_PERFMON is not set */ - ret =cap_enable_effective (1ULL <<CAP_BPF |1ULL <<CAP_NET_ADMIN , &caps); -if (!ASSERT_OK (ret,"set_cap_bpf_cap_net_admin" )) -return ; - ret =cap_disable_effective (1ULL <<CAP_SYS_ADMIN |1ULL <<CAP_PERFMON ,NULL ); -if (!ASSERT_OK (ret,"disable_cap_sys_admin" )) - goto restore_cap; -RUN (verifier_mtu); -restore_cap: -if (caps) -cap_enable_effective (caps,NULL ); -} +void test_verifier_mtu (void ) {RUN (verifier_mtu); }static int init_test_val_map (struct bpf_object *obj,char *map_name) {
tools/testing/selftests/bpf/progs/bpf_misc.h +12 -0
@@ -5,6 +5,10 @@#define XSTR (s) STR(s)#define STR (s) #s +/* Expand a macro and then stringize the expansion */ +#define QUOTE (str) #str +#define EXPAND_QUOTE (str) QUOTE(str) +/* This set of attributes controls behavior of the * test_loader.c:test_loader__run_subtests(). *
@@ -106,6 +110,7 @@* __arch_* Specify on which architecture the test case should be tested. * Several __arch_* annotations could be specified at once. * When test case is not run on current arch it is marked as skipped. +* __caps_unpriv Specify the capabilities that should be set when running the test. */ #define __msg (msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))#define __xlated (msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
@@ -129,6 +134,13 @@#define __arch_x86_64 __arch("X86_64")#define __arch_arm64 __arch("ARM64")#define __arch_riscv64 __arch("RISCV64") +#define __caps_unpriv (caps) __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps)))) + +/* Define common capabilities tested using __caps_unpriv */ +#define CAP_NET_ADMIN 12 +#define CAP_SYS_ADMIN 21 +#define CAP_PERFMON 38 +#define CAP_BPF 39/* Convenience macro for use with 'asm volatile' blocks */ #define __naked __attribute__((naked))
tools/testing/selftests/bpf/progs/verifier_mtu.c +3 -1
@@ -6,7 +6,9 @@ SEC("tc/ingress") __description("uninit/mtu: write rejected") -__failure __msg("invalid indirect read from stack") +__success +__caps_unpriv(CAP_BPF|CAP_NET_ADMIN) +__failure_unpriv __msg_unpriv("invalid indirect read from stack") int tc_uninit_mtu(struct __sk_buff *ctx) {__u32 mtu;
tools/testing/selftests/bpf/test_loader.c +46 -0
76 lines not on this page · raw message
Signature · 1 line
2.43.5
Kumar Kartikeya Dwivedi
maintainerPATCH v4 4/5 selftests/bpf: Add test for reading from STACK_INVALID slots
In reply to Kumar Kartikeya DwivediEnsure that when CAP_PERFMON is dropped, and the verifier sees allow_ptr_leaks as false, we are not permitted to read from a STACK_INVALID slot. Without the fix, the test will report unexpected success in loading.
--- .../selftests/bpf/progs/verifier_spill_fill.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
tools/testing/selftests/bpf/progs/verifier_spill_fill.c +18 -0
@@ -1244,4 +1244,22 @@ __naked void old_stack_misc_vs_cur_ctx_ptr(void) : __clobber_all); } +SEC("socket") +__description("stack_noperfmon: reject read of invalid slots") +__success +__caps_unpriv(CAP_BPF) +__failure_unpriv __msg_unpriv("invalid read from stack off -8+1 size 8") +__naked void stack_noperfmon_reject_invalid_read (void ) +{ + asmvolatile (" \ +r2 = 1; \ +r6 = r10; \ +r6 += -8; \ +*(u8 *)(r6 + 0) = r2; \ +r2 = *(u64 *)(r6 + 0); \ +r0 = 0; \ +exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL";
Signature · 1 line
2.43.5
Kumar Kartikeya Dwivedi
maintainerPATCH v4 5/5 selftests/bpf: Add test for narrow spill into 64-bit spilled scalar
In reply to Kumar Kartikeya DwivediAdd a test case to verify that without CAP_PERFMON, the test now succeeds instead of failing due to a verification error.
--- .../selftests/bpf/progs/verifier_spill_fill.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
tools/testing/selftests/bpf/progs/verifier_spill_fill.c +17 -0
@@ -1262,4 +1262,21 @@ __naked void stack_noperfmon_reject_invalid_read(void) " ::: __clobber_all); } +SEC("socket") +__description("stack_noperfmon: narrow spill onto 64-bit scalar spilled slots") +__success +__caps_unpriv (CAP_BPF ) +__success_unpriv +__naked voidstack_noperfmon_spill_32bit_onto_64bit_slot (void) +{ + asmvolatile (" \ +r0 = 0; \ +*(u64 *)(r10 - 8) = r0; \ +*(u32 *)(r10 - 8) = r0; \ +exit; \ +" : + : + : __clobber_all); +} + char _license[] SEC("license") = "GPL";
Signature · 1 line
2.43.5
This series was applied to bpf/bpf.git (master) by Alexei Starovoitov <[email protected]>:
Hello:
This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <[email protected]>:
Quoted 10 lines · On Tue, 3 Dec 2024 20:47:52 -0800 you wrote:
Two fixes for usability/correctness gaps when interacting with the stack without
CAP_PERFMON(i.e. withallow_ptr_leaks= false). See the commits for details. I've verified that the tests fail when run without the fixes.Changelog:
----------
v3 -> v4
v3: https://lore.kernel.org/bpf/[email protected][...]
Here is the summary with links:
- [bpf,v4,1/5] bpf: Don't mark STACK_INVALID as STACK_MISC in mark_stack_slot_misc
https://git.kernel.org/bpf/bpf/c/69772f509e08 - [bpf,v4,2/5] bpf: Fix narrow scalar spill onto 64-bit spilled scalar slots https://git.kernel.org/bpf/bpf/c/b0e66977dc07 - [bpf,v4,3/5] selftests/bpf: Introduce __caps_unpriv annotation for tests https://git.kernel.org/bpf/bpf/c/adfdd9c68566 - [bpf,v4,4/5] selftests/bpf: Add test for reading from STACK_INVALID slots https://git.kernel.org/bpf/bpf/c/f513c3635078 - [bpf,v4,5/5] selftests/bpf: Add test for narrow spill into 64-bit spilled scalar https://git.kernel.org/bpf/bpf/c/19b6dbc006ec
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html