selftests/bpf: Test CO-RE instruction poisoning restrictions

Add raw CO-RE relocations that fail to resolve their target enum value.
Place each supported and unsupported instruction form in dead code.
Unsupported targets must fail relocation with a diagnostic even when they
are unreachable. Supported ALU immediates, memory accesses, and ldimm64
instructions must still be poisoned and removed as dead code, allowing the
program to load. Check that both halves of ldimm64 are poisoned.

Load every instruction stream without relocations first to ensure that
rejection is caused by the relocation rather than the original program.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://patch.msgid.link/20260917233222.2542500-8-memxor@gmail.com
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi 2026-09-18 01:32:15 +02:00 committed by Eduard Zingerman
parent 394ae39833
commit 3440505aca

View File

@ -50,6 +50,28 @@ static int load_core_relo_insns(int btf_fd, struct bpf_insn *insns, int insn_cnt
static void test_early_core_relo(void)
{
static const char unrecognized[] = "trying to relocate unrecognized insn #2";
static const struct {
const char *name;
struct bpf_insn insns[2];
const char *err_msg;
} tests[] = {
{ "poison_exit", { BPF_EXIT_INSN() }, unrecognized },
{ "poison_ja", { BPF_JMP_A(1) }, unrecognized },
{ "poison_jmp", { BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1) }, unrecognized },
{ "poison_jmp32", { BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 0, 1) }, unrecognized },
{ "poison_call", { BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32) }, unrecognized },
{ "poison_alu_reg", { BPF_MOV32_REG(BPF_REG_0, BPF_REG_1) }, unrecognized },
{ "poison_alu64_reg", { BPF_MOV64_REG(BPF_REG_0, BPF_REG_1) }, unrecognized },
{ "poison_ld_abs", { BPF_LD_ABS(BPF_W, 0) },
"insn #2 (LDIMM64) has unexpected form" },
{ "poison_alu_imm", { BPF_MOV32_IMM(BPF_REG_0, 0) } },
{ "poison_alu64_imm", { BPF_MOV64_IMM(BPF_REG_0, 0) } },
{ "poison_ldx", { BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0) } },
{ "poison_st", { BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0) } },
{ "poison_stx", { BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4) } },
{ "poison_ldimm64", { BPF_LD_IMM64(BPF_REG_0, 0) } },
};
struct test_btf {
struct btf_header hdr;
__u32 types[18];
@ -97,7 +119,7 @@ static void test_early_core_relo(void)
};
int access_str_off = 51; /* offset of "0" */
int enum_id = 5;
int btf_fd, prog_fd = -1;
int btf_fd, prog_fd = -1, i;
btf_fd = bpf_btf_load(&raw_btf, sizeof(raw_btf), NULL);
if (!ASSERT_GE(btf_fd, 0, "btf_load"))
@ -136,6 +158,43 @@ static void test_early_core_relo(void)
ASSERT_HAS_SUBSTR(log, "invalid bpf_ld_imm64 insn", "truncated_load_log");
}
for (i = 0; i < ARRAY_SIZE(tests); i++) {
struct bpf_insn insns[] = {
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
tests[i].insns[0],
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
bool is_ldimm64 = insns[2].code == (BPF_LD | BPF_DW | BPF_IMM);
if (!test__start_subtest(tests[i].name))
continue;
if (is_ldimm64) {
insns[1].off = 2;
insns[3] = tests[i].insns[1];
}
prog_fd = load_core_relo_insns(btf_fd, insns, ARRAY_SIZE(insns), funcs, 1,
enum_id, access_str_off, 2, false);
if (!ASSERT_GE(prog_fd, 0, "control_load"))
goto cleanup;
close(prog_fd);
prog_fd = load_core_relo_insns(btf_fd, insns, ARRAY_SIZE(insns), funcs, 1,
enum_id, access_str_off, 2, true);
if (!tests[i].err_msg) {
ASSERT_GE(prog_fd, 0, "dead_poison_load");
ASSERT_HAS_SUBSTR(log, "substituting insn #2", "poison_log");
if (is_ldimm64)
ASSERT_HAS_SUBSTR(log, "substituting insn #3", "poison_ldimm64_log");
} else {
ASSERT_LT(prog_fd, 0, "invalid_poison_load");
ASSERT_HAS_SUBSTR(log, tests[i].err_msg, "invalid_poison_log");
ASSERT_NULL(strstr(log, "substituting insn"), "invalid_poison_substitution");
}
close(prog_fd);
prog_fd = -1;
}
cleanup:
if (env.verbosity > VERBOSE_NORMAL && log[0]) {
printf("-------- program load log start --------\n");