selftests/bpf: Add load conditions on the program stack limit

The stack a program may use will depend on the JIT: 2 KiB where the JIT
declares support for large stacks, 512 bytes elsewhere and for
interpreted programs. Tests that probe the limit therefore need to know
which one is in force. Add __load_if_large_stack() and
__load_if_no_large_stack() to test_loader, analogous to the JIT load
conditions, backed by a one-time probe that loads a program storing at
fp-2048. The probe caches only the verifier's verdict on that store: a
load that fails for another reason, such as a missing capability, is
reported and probed again on the next call.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20260924165740.2146806-15-memxor@gmail.com
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-09-24 18:57:15 +02:00 committed by Alexei Starovoitov
parent 9cfc28c9a8
commit 4fc52c1b54
No known key found for this signature in database
4 changed files with 69 additions and 0 deletions

View File

@ -175,6 +175,9 @@
#define __prepare_priv __test_tag("test_prepare_priv")
#define __load_if_JITed() __test_tag("load_mode=jited")
#define __load_if_no_JITed() __test_tag("load_mode=no_jited")
/* Whether programs may use more than 512 bytes of stack on this kernel and JIT */
#define __load_if_large_stack() __test_tag("stack_mode=large")
#define __load_if_no_large_stack() __test_tag("stack_mode=small")
#define __stderr(msg) __test_tag("test_expect_stderr=" msg)
#define __stderr_unpriv(msg) __test_tag("test_expect_stderr_unpriv=" msg)
#define __stdout(msg) __test_tag("test_expect_stdout=" msg)

View File

@ -45,6 +45,11 @@ enum load_mode {
NO_JITED = 1 << 1,
};
enum stack_mode {
LARGE_STACK = 1 << 0,
SMALL_STACK = 1 << 1,
};
struct test_subspec {
char *name;
char *description;
@ -70,6 +75,7 @@ struct test_spec {
int mode_mask;
int arch_mask;
int load_mask;
int stack_mask;
int linear_sz;
const char *skip_reason;
bool prepare_priv;
@ -425,6 +431,7 @@ static int parse_test_spec(struct test_loader *tester,
int err = 0;
u32 arch_mask = 0;
u32 load_mask = 0;
u32 stack_mask = 0;
struct btf *btf;
enum arch arch;
@ -620,6 +627,16 @@ static int parse_test_spec(struct test_loader *tester,
err = -EINVAL;
goto cleanup;
}
} else if ((val = str_has_pfx(s, "stack_mode="))) {
if (strcmp(val, "large") == 0) {
stack_mask = LARGE_STACK;
} else if (strcmp(val, "small") == 0) {
stack_mask = SMALL_STACK;
} else {
PRINT_FAIL("bad stack spec: '%s'", val);
err = -EINVAL;
goto cleanup;
}
} else if ((msg = str_has_pfx(s, "test_expect_stderr="))) {
err = push_disasm_msg(msg, &stderr_on_next_line,
&spec->priv.stderr);
@ -659,6 +676,7 @@ static int parse_test_spec(struct test_loader *tester,
spec->arch_mask = arch_mask ?: -1;
spec->load_mask = load_mask ?: (JITED | NO_JITED);
spec->stack_mask = stack_mask ?: (LARGE_STACK | SMALL_STACK);
if (spec->mode_mask == 0)
spec->mode_mask = PRIV;
@ -1331,6 +1349,7 @@ void run_subtest(struct test_loader *tester,
{
struct test_subspec *subspec = unpriv ? &spec->unpriv : &spec->priv;
int current_runtime = is_jit_enabled() ? JITED : NO_JITED;
int current_stack = is_large_stack_supported() ? LARGE_STACK : SMALL_STACK;
struct bpf_program *tprog = NULL, *tprog_iter;
struct bpf_link *link, *links[32] = {};
struct test_spec *spec_iter;
@ -1360,6 +1379,11 @@ void run_subtest(struct test_loader *tester,
return;
}
if ((current_stack & spec->stack_mask) == 0) {
test__skip();
return;
}
if (unpriv) {
if (!can_execute_unpriv(tester, spec)) {
test__skip();

View File

@ -517,6 +517,47 @@ bool is_jit_enabled(void)
return enabled;
}
/*
* Whether the kernel accepts a program using more than 512 bytes of stack,
* which depends on the JIT in use. Probed once with a program that stores
* at the 2 KiB depth. Only the verifier's verdict on that store is cached:
* a load that fails for another reason, such as a missing capability, is
* reported and probed again on the next call.
*/
bool is_large_stack_supported(void)
{
static int supported = -1;
struct bpf_insn insns[] = {
BPF_ST_MEM(BPF_DW, BPF_REG_10, -2048, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
char log[1024] = {};
LIBBPF_OPTS(bpf_prog_load_opts, opts,
.log_buf = log,
.log_size = sizeof(log),
.log_level = 1,
);
int fd;
if (supported >= 0)
return supported;
fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, ARRAY_SIZE(insns),
&opts);
if (fd >= 0) {
close(fd);
supported = 1;
} else if (strstr(log, "invalid write to stack")) {
supported = 0;
} else {
fprintf(stderr, "%s: probe failed with errno %d, assuming 512 bytes:\n%s",
__func__, errno, log);
return false;
}
return supported;
}
int stack_mprotect(void)
{
void *buf;

View File

@ -59,6 +59,7 @@ struct bpf_insn;
int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt);
int testing_prog_flags(void);
bool is_jit_enabled(void);
bool is_large_stack_supported(void);
int stack_mprotect(void);
/* Runs diff(1) on mismatch */