Skip to content

Commit 4811c04

Browse files
committed
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). On x86_64, this depends on the following property of lfence [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. 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. Briefly went through all the occurrences of EPERM, EINVAL, and EACCESS in the verifier in order to validate that catching them like this makes sense. [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") Signed-off-by: Luis Gerhorst <luis.gerhorst@fau.de> Acked-by: Henriette Herzog <henriette.herzog@rub.de> Cc: Maximilian Ott <ott@cs.fau.de> Cc: Milan Stephan <milan.stephan@fau.de>
1 parent 9e307b7 commit 4811c04

File tree

11 files changed

+184
-54
lines changed

11 files changed

+184
-54
lines changed

include/linux/bpf_verifier.h

+1
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ struct bpf_insn_aux_data {
576576
u64 map_key_state; /* constant (32 bit) key tracking for maps */
577577
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
578578
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
579+
bool nospec; /* do not execute this instruction speculatively */
579580
bool nospec_result; /* result is unsafe under speculation, nospec must follow */
580581
bool zext_dst; /* this insn zero extends dst reg */
581582
bool needs_zext; /* alu op needs to clear upper bits */

kernel/bpf/verifier.c

+74-4
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,18 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
20142014
return 0;
20152015
}
20162016

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

11163-
static struct bpf_insn_aux_data *cur_aux(struct bpf_verifier_env *env)
11175+
static struct bpf_insn_aux_data *cur_aux(const struct bpf_verifier_env *env)
1116411176
{
1116511177
return &env->insn_aux_data[env->insn_idx];
1116611178
}
@@ -13997,7 +14009,9 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
1399714009
static bool can_skip_alu_sanitation(const struct bpf_verifier_env *env,
1399814010
const struct bpf_insn *insn)
1399914011
{
14000-
return env->bypass_spec_v1 || BPF_SRC(insn->code) == BPF_K;
14012+
return env->bypass_spec_v1 ||
14013+
BPF_SRC(insn->code) == BPF_K ||
14014+
cur_aux(env)->nospec;
1400114015
}
1400214016

1400314017
static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux,
@@ -19687,10 +19701,41 @@ static int do_check(struct bpf_verifier_env *env)
1968719701
sanitize_mark_insn_seen(env);
1968819702
prev_insn_idx = env->insn_idx;
1968919703

19704+
/* Reduce verification complexity by stopping speculative path
19705+
* verification when a nospec is encountered.
19706+
*/
19707+
if (state->speculative && cur_aux(env)->nospec)
19708+
goto process_bpf_exit;
19709+
1969019710
err = do_check_insn(env, insn, &do_print_state);
19691-
if (err < 0) {
19711+
if (state->speculative && error_recoverable_with_nospec(err)) {
19712+
/* Prevent this speculative path from ever reaching the
19713+
* insn that would have been unsafe to execute.
19714+
*/
19715+
cur_aux(env)->nospec = true;
19716+
/* If it was an ADD/SUB insn, potentially remove any
19717+
* markings for alu sanitization.
19718+
*/
19719+
cur_aux(env)->alu_state = 0;
19720+
goto process_bpf_exit;
19721+
} else if (err < 0) {
1969219722
return err;
1969319723
} else if (err == PROCESS_BPF_EXIT) {
19724+
goto process_bpf_exit;
19725+
}
19726+
WARN_ON_ONCE(err);
19727+
19728+
if (state->speculative && cur_aux(env)->nospec_result) {
19729+
/* If we are on a path that performed a jump-op, this
19730+
* may skip a nospec patched-in after the jump. This can
19731+
* currently never happen because nospec_result is only
19732+
* used for the write-ops
19733+
* `*(size*)(dst_reg+off)=src_reg|imm32` which must
19734+
* never skip the following insn. Still, add a warning
19735+
* to document this in case nospec_result is used
19736+
* elsewhere in the future.
19737+
*/
19738+
WARN_ON_ONCE(env->insn_idx != prev_insn_idx + 1);
1969419739
process_bpf_exit:
1969519740
mark_verifier_state_scratched(env);
1969619741
update_branch_counts(env, env->cur_state);
@@ -19709,7 +19754,6 @@ static int do_check(struct bpf_verifier_env *env)
1970919754
continue;
1971019755
}
1971119756
}
19712-
WARN_ON_ONCE(err);
1971319757
}
1971419758

1971519759
return 0;
@@ -20838,6 +20882,29 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
2083820882
bpf_convert_ctx_access_t convert_ctx_access;
2083920883
u8 mode;
2084020884

20885+
if (env->insn_aux_data[i + delta].nospec) {
20886+
WARN_ON_ONCE(env->insn_aux_data[i + delta].alu_state);
20887+
struct bpf_insn patch[] = {
20888+
BPF_ST_NOSPEC(),
20889+
*insn,
20890+
};
20891+
20892+
cnt = ARRAY_SIZE(patch);
20893+
new_prog = bpf_patch_insn_data(env, i + delta, patch, cnt);
20894+
if (!new_prog)
20895+
return -ENOMEM;
20896+
20897+
delta += cnt - 1;
20898+
env->prog = new_prog;
20899+
insn = new_prog->insnsi + i + delta;
20900+
/* This can not be easily merged with the
20901+
* nospec_result-case, because an insn may require a
20902+
* nospec before and after itself. Therefore also do not
20903+
* 'continue' here but potentially apply further
20904+
* patching to insn. *insn should equal patch[1] now.
20905+
*/
20906+
}
20907+
2084120908
if (insn->code == (BPF_LDX | BPF_MEM | BPF_B) ||
2084220909
insn->code == (BPF_LDX | BPF_MEM | BPF_H) ||
2084320910
insn->code == (BPF_LDX | BPF_MEM | BPF_W) ||
@@ -20888,6 +20955,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
2088820955

2088920956
if (type == BPF_WRITE &&
2089020957
env->insn_aux_data[i + delta].nospec_result) {
20958+
/* nospec_result is only used to mitigate Spectre v4 and
20959+
* to limit verification-time for Spectre v1.
20960+
*/
2089120961
struct bpf_insn patch[] = {
2089220962
*insn,
2089320963
BPF_ST_NOSPEC(),

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

+4
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,8 @@
230230
#define CAN_USE_LOAD_ACQ_STORE_REL
231231
#endif
232232

233+
#if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)
234+
#define SPEC_V1
235+
#endif
236+
233237
#endif

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

+7-1
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

+49-12
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

+14-2
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

+7-1
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

+12-4
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

+1-2
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)