Skip to content

Commit d6f1c85

Browse files
luisgerhorstAlexei Starovoitov
authored andcommitted
bpf: Fall back to nospec for Spectre v1
This implements the core of the series and causes the verifier to fall back to mitigating Spectre v1 using speculation barriers. The approach was presented at LPC'24 [1] and RAID'24 [2]. If we find any forbidden behavior on a speculative path, we insert a nospec (e.g., lfence speculation barrier on x86) before the instruction and stop verifying the path. While verifying a speculative path, we can furthermore stop verification of that path whenever we encounter a nospec instruction. A minimal example program would look as follows: A = true B = true if A goto e f() if B goto e unsafe() e: exit There are the following speculative and non-speculative paths (`cur->speculative` and `speculative` referring to the value of the push_stack() parameters): - A = true - B = true - if A goto e - A && !cur->speculative && !speculative - exit - !A && !cur->speculative && speculative - f() - if B goto e - B && cur->speculative && !speculative - exit - !B && cur->speculative && speculative - unsafe() If f() contains any unsafe behavior under Spectre v1 and the unsafe behavior matches `state->speculative && error_recoverable_with_nospec(err)`, do_check() will now add a nospec before f() instead of rejecting the program: A = true B = true if A goto e nospec f() if B goto e unsafe() e: exit Alternatively, the algorithm also takes advantage of nospec instructions inserted for other reasons (e.g., Spectre v4). Taking the program above as an example, speculative path exploration can stop before f() if a nospec was inserted there because of Spectre v4 sanitization. In this example, all instructions after the nospec are dead code (and with the nospec they are also dead code speculatively). For this, it relies on the fact that speculation barriers generally prevent all later instructions from executing if the speculation was not correct: * On Intel x86_64, lfence acts as full speculation barrier, not only as a load fence [3]: An LFENCE instruction or a serializing instruction will ensure that no later instructions execute, even speculatively, until all prior instructions complete locally. [...] Inserting an LFENCE instruction after a bounds check prevents later operations from executing before the bound check completes. This was experimentally confirmed in [4]. * On AMD x86_64, lfence is dispatch-serializing [5] (requires MSR C001_1029[1] to be set if the MSR is supported, this happens in init_amd()). AMD further specifies "A dispatch serializing instruction forces the processor to retire the serializing instruction and all previous instructions before the next instruction is executed" [8]. As dispatch is not specific to memory loads or branches, lfence therefore also affects all instructions there. Also, if retiring a branch means it's PC change becomes architectural (should be), this means any "wrong" speculation is aborted as required for this series. * ARM's SB speculation barrier instruction also affects "any instruction that appears later in the program order than the barrier" [6]. * PowerPC's barrier also affects all subsequent instructions [7]: [...] executing an ori R31,R31,0 instruction ensures that all instructions preceding the ori R31,R31,0 instruction have completed before the ori R31,R31,0 instruction completes, and that no subsequent instructions are initiated, even out-of-order, until after the ori R31,R31,0 instruction completes. The ori R31,R31,0 instruction may complete before storage accesses associated with instructions preceding the ori R31,R31,0 instruction have been performed Regarding the example, this implies that `if B goto e` will not execute before `if A goto e` completes. Once `if A goto e` completes, the CPU should find that the speculation was wrong and continue with `exit`. If there is any other path that leads to `if B goto e` (and therefore `unsafe()`) without going through `if A goto e`, then a nospec will still be needed there. However, this patch assumes this other path will be explored separately and therefore be discovered by the verifier even if the exploration discussed here stops at the nospec. This patch furthermore has the unfortunate consequence that Spectre v1 mitigations now only support architectures which implement BPF_NOSPEC. Before this commit, Spectre v1 mitigations prevented exploits by rejecting the programs on all architectures. Because some JITs do not implement BPF_NOSPEC, this patch therefore may regress unpriv BPF's security to a limited extent: * The regression is limited to systems vulnerable to Spectre v1, have unprivileged BPF enabled, and do NOT emit insns for BPF_NOSPEC. The latter is not the case for x86 64- and 32-bit, arm64, and powerpc 64-bit and they are therefore not affected by the regression. According to commit a6f6a95 ("LoongArch, bpf: Fix jit to skip speculation barrier opcode"), LoongArch is not vulnerable to Spectre v1 and therefore also not affected by the regression. * To the best of my knowledge this regression may therefore only affect MIPS. This is deemed acceptable because unpriv BPF is still disabled there by default. As stated in a previous commit, BPF_NOSPEC could be implemented for MIPS based on GCC's speculation_barrier implementation. * It is unclear which other architectures (besides x86 64- and 32-bit, ARM64, PowerPC 64-bit, LoongArch, and MIPS) supported by the kernel are vulnerable to Spectre v1. Also, it is not clear if barriers are available on these architectures. Implementing BPF_NOSPEC on these architectures therefore is non-trivial. Searching GCC and the kernel for speculation barrier implementations for these architectures yielded no result. * If any of those regressed systems is also vulnerable to Spectre v4, the system was already vulnerable to Spectre v4 attacks based on unpriv BPF before this patch and the impact is therefore further limited. As an alternative to regressing security, one could still reject programs if the architecture does not emit BPF_NOSPEC (e.g., by removing the empty BPF_NOSPEC-case from all JITs except for LoongArch where it appears justified). However, this will cause rejections on these archs that are likely unfounded in the vast majority of cases. In the tests, some are now successful where we previously had a false-positive (i.e., rejection). Change them to reflect where the nospec should be inserted (using __xlated_unpriv) and modify the error message if the nospec is able to mitigate a problem that previously shadowed another problem (in that case __xlated_unpriv does not work, therefore just add a comment). Define SPEC_V1 to avoid duplicating this ifdef whenever we check for nospec insns using __xlated_unpriv, define it here once. This also improves readability. PowerPC can probably also be added here. However, omit it for now because the BPF CI currently does not include a test. Limit it to EPERM, EACCES, and EINVAL (and not everything except for EFAULT and ENOMEM) as it already has the desired effect for most real-world programs. Briefly went through all the occurrences of EPERM, EINVAL, and EACCESS in verifier.c to validate that catching them like this makes sense. Thanks to Dustin for their help in checking the vendor documentation. [1] https://lpc.events/event/18/contributions/1954/ ("Mitigating Spectre-PHT using Speculation Barriers in Linux eBPF") [2] https://arxiv.org/pdf/2405.00078 ("VeriFence: Lightweight and Precise Spectre Defenses for Untrusted Linux Kernel Extensions") [3] https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/runtime-speculative-side-channel-mitigations.html ("Managed Runtime Speculative Execution Side Channel Mitigations") [4] https://dl.acm.org/doi/pdf/10.1145/3359789.3359837 ("Speculator: a tool to analyze speculative execution attacks and mitigations" - Section 4.6 "Stopping Speculative Execution") [5] https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/software-techniques-for-managing-speculation.pdf ("White Paper - SOFTWARE TECHNIQUES FOR MANAGING SPECULATION ON AMD PROCESSORS - REVISION 5.09.23") [6] https://developer.arm.com/documentation/ddi0597/2020-12/Base-Instructions/SB--Speculation-Barrier- ("SB - Speculation Barrier - Arm Armv8-A A32/T32 Instruction Set Architecture (2020-12)") [7] https://wiki.raptorcs.com/w/images/5/5f/OPF_PowerISA_v3.1C.pdf ("Power ISA™ - Version 3.1C - May 26, 2024 - Section 9.2.1 of Book III") [8] https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/40332.pdf ("AMD64 Architecture Programmer’s Manual Volumes 1–5 - Revision 4.08 - April 2024 - 7.6.4 Serializing Instructions") Signed-off-by: Luis Gerhorst <[email protected]> Acked-by: Kumar Kartikeya Dwivedi <[email protected]> Acked-by: Henriette Herzog <[email protected]> Cc: Dustin Nguyen <[email protected]> Cc: Maximilian Ott <[email protected]> Cc: Milan Stephan <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 9124a45 commit d6f1c85

File tree

11 files changed

+184
-54
lines changed

11 files changed

+184
-54
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ struct bpf_insn_aux_data {
580580
u64 map_key_state; /* constant (32 bit) key tracking for maps */
581581
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
582582
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
583+
bool nospec; /* do not execute this instruction speculatively */
583584
bool nospec_result; /* result is unsafe under speculation, nospec must follow */
584585
bool zext_dst; /* this insn zero extends dst reg */
585586
bool needs_zext; /* alu op needs to clear upper bits */

kernel/bpf/verifier.c

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,6 +2013,18 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
20132013
return 0;
20142014
}
20152015

2016+
static bool error_recoverable_with_nospec(int err)
2017+
{
2018+
/* Should only return true for non-fatal errors that are allowed to
2019+
* occur during speculative verification. For these we can insert a
2020+
* nospec and the program might still be accepted. Do not include
2021+
* something like ENOMEM because it is likely to re-occur for the next
2022+
* architectural path once it has been recovered-from in all speculative
2023+
* paths.
2024+
*/
2025+
return err == -EPERM || err == -EACCES || err == -EINVAL;
2026+
}
2027+
20162028
static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
20172029
int insn_idx, int prev_insn_idx,
20182030
bool speculative)
@@ -11147,7 +11159,7 @@ static int check_get_func_ip(struct bpf_verifier_env *env)
1114711159
return -ENOTSUPP;
1114811160
}
1114911161

11150-
static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
11162+
static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
1115111163
{
1115211164
return &env->insn_aux_data[env->insn_idx];
1115311165
}
@@ -14015,7 +14027,9 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
1401514027
static bool can_skip_alu_sanitation(const struct bpf_verifier_env *env,
1401614028
const struct bpf_insn *insn)
1401714029
{
14018-
return env->bypass_spec_v1 || BPF_SRC(insn->code) == BPF_K;
14030+
return env->bypass_spec_v1 ||
14031+
BPF_SRC(insn->code) == BPF_K ||
14032+
cur_aux(env)->nospec;
1401914033
}
1402014034

1402114035
static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux,
@@ -19732,10 +19746,41 @@ static int do_check(struct bpf_verifier_env *env)
1973219746
sanitize_mark_insn_seen(env);
1973319747
prev_insn_idx = env->insn_idx;
1973419748

19749+
/* Reduce verification complexity by stopping speculative path
19750+
* verification when a nospec is encountered.
19751+
*/
19752+
if (state->speculative && cur_aux(env)->nospec)
19753+
goto process_bpf_exit;
19754+
1973519755
err = do_check_insn(env, &do_print_state);
19736-
if (err < 0) {
19756+
if (state->speculative && error_recoverable_with_nospec(err)) {
19757+
/* Prevent this speculative path from ever reaching the
19758+
* insn that would have been unsafe to execute.
19759+
*/
19760+
cur_aux(env)->nospec = true;
19761+
/* If it was an ADD/SUB insn, potentially remove any
19762+
* markings for alu sanitization.
19763+
*/
19764+
cur_aux(env)->alu_state = 0;
19765+
goto process_bpf_exit;
19766+
} else if (err < 0) {
1973719767
return err;
1973819768
} else if (err == PROCESS_BPF_EXIT) {
19769+
goto process_bpf_exit;
19770+
}
19771+
WARN_ON_ONCE(err);
19772+
19773+
if (state->speculative && cur_aux(env)->nospec_result) {
19774+
/* If we are on a path that performed a jump-op, this
19775+
* may skip a nospec patched-in after the jump. This can
19776+
* currently never happen because nospec_result is only
19777+
* used for the write-ops
19778+
* `*(size*)(dst_reg+off)=src_reg|imm32` which must
19779+
* never skip the following insn. Still, add a warning
19780+
* to document this in case nospec_result is used
19781+
* elsewhere in the future.
19782+
*/
19783+
WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1);
1973919784
process_bpf_exit:
1974019785
mark_verifier_state_scratched(env);
1974119786
update_branch_counts(env, env->cur_state);
@@ -19753,7 +19798,6 @@ static int do_check(struct bpf_verifier_env *env)
1975319798
continue;
1975419799
}
1975519800
}
19756-
WARN_ON_ONCE(err);
1975719801
}
1975819802

1975919803
return 0;
@@ -20881,6 +20925,29 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
2088120925
bpf_convert_ctx_access_t convert_ctx_access;
2088220926
u8 mode;
2088320927

20928+
if (env->insn_aux_data[i + delta].nospec) {
20929+
WARN_ON_ONCE(env->insn_aux_data[i + delta].alu_state);
20930+
struct bpf_insn patch[] = {
20931+
BPF_ST_NOSPEC(),
20932+
*insn,
20933+
};
20934+
20935+
cnt = ARRAY_SIZE(patch);
20936+
new_prog = bpf_patch_insn_data(env, i + delta, patch, cnt);
20937+
if (!new_prog)
20938+
return -ENOMEM;
20939+
20940+
delta += cnt - 1;
20941+
env->prog = new_prog;
20942+
insn = new_prog->insnsi + i + delta;
20943+
/* This can not be easily merged with the
20944+
* nospec_result-case, because an insn may require a
20945+
* nospec before and after itself. Therefore also do not
20946+
* 'continue' here but potentially apply further
20947+
* patching to insn. *insn should equal patch[1] now.
20948+
*/
20949+
}
20950+
2088420951
if (insn->code == (BPF_LDX | BPF_MEM | BPF_B) ||
2088520952
insn->code == (BPF_LDX | BPF_MEM | BPF_H) ||
2088620953
insn->code == (BPF_LDX | BPF_MEM | BPF_W) ||
@@ -20931,6 +20998,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
2093120998

2093220999
if (type == BPF_WRITE &&
2093321000
env->insn_aux_data[i + delta].nospec_result) {
21001+
/* nospec_result is only used to mitigate Spectre v4 and
21002+
* to limit verification-time for Spectre v1.
21003+
*/
2093421004
struct bpf_insn patch[] = {
2093521005
*insn,
2093621006
BPF_ST_NOSPEC(),

tools/testing/selftests/bpf/progs/bpf_misc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,4 +231,8 @@
231231
#define CAN_USE_LOAD_ACQ_STORE_REL
232232
#endif
233233

234+
#if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)
235+
#define SPEC_V1
236+
#endif
237+
234238
#endif

tools/testing/selftests/bpf/progs/verifier_and.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,14 @@ l0_%=: r0 = r0; \
8585

8686
SEC("socket")
8787
__description("check known subreg with unknown reg")
88-
__success __failure_unpriv __msg_unpriv("R1 !read_ok")
88+
__success __success_unpriv
8989
__retval(0)
90+
#ifdef SPEC_V1
91+
__xlated_unpriv("if w0 < 0x1 goto pc+2")
92+
__xlated_unpriv("nospec") /* inserted to prevent `R1 !read_ok'` */
93+
__xlated_unpriv("goto pc-1") /* `r1 = *(u32*)(r1 + 512)`, sanitized dead code */
94+
__xlated_unpriv("r0 = 0")
95+
#endif
9096
__naked void known_subreg_with_unknown_reg(void)
9197
{
9298
asm volatile (" \

tools/testing/selftests/bpf/progs/verifier_bounds.c

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -620,8 +620,14 @@ l1_%=: exit; \
620620

621621
SEC("socket")
622622
__description("bounds check mixed 32bit and 64bit arithmetic. test1")
623-
__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'")
623+
__success __success_unpriv
624624
__retval(0)
625+
#ifdef SPEC_V1
626+
__xlated_unpriv("goto pc+2")
627+
__xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */
628+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
629+
__xlated_unpriv("exit")
630+
#endif
625631
__naked void _32bit_and_64bit_arithmetic_test1(void)
626632
{
627633
asm volatile (" \
@@ -643,8 +649,14 @@ l1_%=: exit; \
643649

644650
SEC("socket")
645651
__description("bounds check mixed 32bit and 64bit arithmetic. test2")
646-
__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'")
652+
__success __success_unpriv
647653
__retval(0)
654+
#ifdef SPEC_V1
655+
__xlated_unpriv("goto pc+2")
656+
__xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */
657+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
658+
__xlated_unpriv("exit")
659+
#endif
648660
__naked void _32bit_and_64bit_arithmetic_test2(void)
649661
{
650662
asm volatile (" \
@@ -691,9 +703,14 @@ l0_%=: r0 = 0; \
691703

692704
SEC("socket")
693705
__description("bounds check for reg = 0, reg xor 1")
694-
__success __failure_unpriv
695-
__msg_unpriv("R0 min value is outside of the allowed memory range")
706+
__success __success_unpriv
696707
__retval(0)
708+
#ifdef SPEC_V1
709+
__xlated_unpriv("if r1 != 0x0 goto pc+2")
710+
__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */
711+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
712+
__xlated_unpriv("r0 = 0")
713+
#endif
697714
__naked void reg_0_reg_xor_1(void)
698715
{
699716
asm volatile (" \
@@ -719,9 +736,14 @@ l1_%=: r0 = 0; \
719736

720737
SEC("socket")
721738
__description("bounds check for reg32 = 0, reg32 xor 1")
722-
__success __failure_unpriv
723-
__msg_unpriv("R0 min value is outside of the allowed memory range")
739+
__success __success_unpriv
724740
__retval(0)
741+
#ifdef SPEC_V1
742+
__xlated_unpriv("if w1 != 0x0 goto pc+2")
743+
__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */
744+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
745+
__xlated_unpriv("r0 = 0")
746+
#endif
725747
__naked void reg32_0_reg32_xor_1(void)
726748
{
727749
asm volatile (" \
@@ -747,9 +769,14 @@ l1_%=: r0 = 0; \
747769

748770
SEC("socket")
749771
__description("bounds check for reg = 2, reg xor 3")
750-
__success __failure_unpriv
751-
__msg_unpriv("R0 min value is outside of the allowed memory range")
772+
__success __success_unpriv
752773
__retval(0)
774+
#ifdef SPEC_V1
775+
__xlated_unpriv("if r1 > 0x0 goto pc+2")
776+
__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */
777+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
778+
__xlated_unpriv("r0 = 0")
779+
#endif
753780
__naked void reg_2_reg_xor_3(void)
754781
{
755782
asm volatile (" \
@@ -829,9 +856,14 @@ l1_%=: r0 = 0; \
829856

830857
SEC("socket")
831858
__description("bounds check for reg > 0, reg xor 3")
832-
__success __failure_unpriv
833-
__msg_unpriv("R0 min value is outside of the allowed memory range")
859+
__success __success_unpriv
834860
__retval(0)
861+
#ifdef SPEC_V1
862+
__xlated_unpriv("if r1 >= 0x0 goto pc+2")
863+
__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */
864+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
865+
__xlated_unpriv("r0 = 0")
866+
#endif
835867
__naked void reg_0_reg_xor_3(void)
836868
{
837869
asm volatile (" \
@@ -858,9 +890,14 @@ l1_%=: r0 = 0; \
858890

859891
SEC("socket")
860892
__description("bounds check for reg32 > 0, reg32 xor 3")
861-
__success __failure_unpriv
862-
__msg_unpriv("R0 min value is outside of the allowed memory range")
893+
__success __success_unpriv
863894
__retval(0)
895+
#ifdef SPEC_V1
896+
__xlated_unpriv("if w1 >= 0x0 goto pc+2")
897+
__xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */
898+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
899+
__xlated_unpriv("r0 = 0")
900+
#endif
864901
__naked void reg32_0_reg32_xor_3(void)
865902
{
866903
asm volatile (" \

tools/testing/selftests/bpf/progs/verifier_movsx.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,13 @@ l0_%=: \
245245
SEC("socket")
246246
__description("MOV32SX, S8, var_off not u32_max, positive after s8 extension")
247247
__success __retval(0)
248-
__failure_unpriv __msg_unpriv("frame pointer is read only")
248+
__success_unpriv
249+
#ifdef SPEC_V1
250+
__xlated_unpriv("w0 = 0")
251+
__xlated_unpriv("exit")
252+
__xlated_unpriv("nospec") /* inserted to prevent `frame pointer is read only` */
253+
__xlated_unpriv("goto pc-1")
254+
#endif
249255
__naked void mov64sx_s32_varoff_2(void)
250256
{
251257
asm volatile (" \
@@ -267,7 +273,13 @@ l0_%=: \
267273
SEC("socket")
268274
__description("MOV32SX, S8, var_off not u32_max, negative after s8 extension")
269275
__success __retval(0)
270-
__failure_unpriv __msg_unpriv("frame pointer is read only")
276+
__success_unpriv
277+
#ifdef SPEC_V1
278+
__xlated_unpriv("w0 = 0")
279+
__xlated_unpriv("exit")
280+
__xlated_unpriv("nospec") /* inserted to prevent `frame pointer is read only` */
281+
__xlated_unpriv("goto pc-1")
282+
#endif
271283
__naked void mov64sx_s32_varoff_3(void)
272284
{
273285
asm volatile (" \

tools/testing/selftests/bpf/progs/verifier_unpriv.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,14 @@ l0_%=: exit; \
572572

573573
SEC("socket")
574574
__description("alu32: mov u32 const")
575-
__success __failure_unpriv __msg_unpriv("R7 invalid mem access 'scalar'")
575+
__success __success_unpriv
576576
__retval(0)
577+
#ifdef SPEC_V1
578+
__xlated_unpriv("if r0 == 0x0 goto pc+2")
579+
__xlated_unpriv("nospec") /* inserted to prevent `R7 invalid mem access 'scalar'` */
580+
__xlated_unpriv("goto pc-1") /* sanitized dead code */
581+
__xlated_unpriv("exit")
582+
#endif
577583
__naked void alu32_mov_u32_const(void)
578584
{
579585
asm volatile (" \

tools/testing/selftests/bpf/progs/verifier_value_ptr_arith.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ l2_%=: r0 = 1; \
398398

399399
SEC("socket")
400400
__description("map access: mixing value pointer and scalar, 1")
401-
__success __failure_unpriv __msg_unpriv("R2 pointer comparison prohibited")
401+
__success __failure_unpriv
402+
__msg_unpriv("R2 tried to add from different maps, paths or scalars, pointer arithmetic with it prohibited for !root")
402403
__retval(0)
403404
__naked void value_pointer_and_scalar_1(void)
404405
{
@@ -433,6 +434,7 @@ l2_%=: /* common instruction */ \
433434
l3_%=: /* branch B */ \
434435
r0 = 0x13371337; \
435436
/* verifier follows fall-through */ \
437+
/* unpriv: nospec (inserted to prevent `R2 pointer comparison prohibited`) */\
436438
if r2 != 0x100000 goto l4_%=; \
437439
r0 = 0; \
438440
exit; \
@@ -450,7 +452,8 @@ l4_%=: /* fake-dead code; targeted from branch A to \
450452

451453
SEC("socket")
452454
__description("map access: mixing value pointer and scalar, 2")
453-
__success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'")
455+
__success __failure_unpriv
456+
__msg_unpriv("R2 tried to add from different maps, paths or scalars, pointer arithmetic with it prohibited for !root")
454457
__retval(0)
455458
__naked void value_pointer_and_scalar_2(void)
456459
{
@@ -492,6 +495,7 @@ l4_%=: /* fake-dead code; targeted from branch A to \
492495
* prevent dead code sanitization, rejected \
493496
* via branch B however \
494497
*/ \
498+
/* unpriv: nospec (inserted to prevent `R0 invalid mem access 'scalar'`) */\
495499
r0 = *(u8*)(r0 + 0); \
496500
r0 = 0; \
497501
exit; \
@@ -1296,9 +1300,13 @@ l0_%=: r0 = 1; \
12961300

12971301
SEC("socket")
12981302
__description("map access: value_ptr -= unknown scalar, 2")
1299-
__success __failure_unpriv
1300-
__msg_unpriv("R0 pointer arithmetic of map value goes out of range")
1303+
__success __success_unpriv
13011304
__retval(1)
1305+
#ifdef SPEC_V1
1306+
__xlated_unpriv("r1 &= 7")
1307+
__xlated_unpriv("nospec") /* inserted to prevent `R0 pointer arithmetic of map value goes out of range` */
1308+
__xlated_unpriv("r0 -= r1")
1309+
#endif
13021310
__naked void value_ptr_unknown_scalar_2_2(void)
13031311
{
13041312
asm volatile (" \

tools/testing/selftests/bpf/verifier/dead_code.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
"dead code: start",
33
.insns = {
44
BPF_JMP_IMM(BPF_JA, 0, 0, 2),
5+
/* unpriv: nospec (inserted to prevent "R9 !read_ok") */
56
BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
67
BPF_JMP_IMM(BPF_JA, 0, 0, 2),
78
BPF_MOV64_IMM(BPF_REG_0, 7),
89
BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 10, -4),
910
BPF_EXIT_INSN(),
1011
},
11-
.errstr_unpriv = "R9 !read_ok",
12-
.result_unpriv = REJECT,
1312
.result = ACCEPT,
1413
.retval = 7,
1514
},

0 commit comments

Comments
 (0)