mirror of
https://github.com/torvalds/linux.git
synced 2026-09-29 04:22:03 +02:00
bpf: Restrict CO-RE poisoning to relocatable instructions
CO-RE relocation records can name any instruction offset. When a
relocation cannot be resolved, bpf_core_patch_insn() currently poisons its
target before checking whether that instruction is a valid relocation
target. Malformed metadata can therefore replace jumps, calls, exits,
register-source arithmetic, or non-immediate loads instead of failing at
the relocation step.
Handle poisoning only after the instruction has passed the same class and
operand-form checks used for a resolved relocation. Route invalid forms
through the existing diagnostic and return a hard error. Keep poisoning
supported instructions, including both halves of a plain ldimm64, so an
unresolved relocation in dead code remains valid.
Extend bpf_core_poison_insn() to poison both halves of ldimm64, and return
its status directly from each validated instruction case. This avoids
routing the success path through a common label and leaves the helper free
to report errors.
The shared relocation code applies this restriction to both libbpf and
in-kernel CO-RE.
Fixes: d7a252708d ("libbpf: Improve handling of failed CO-RE relocations")
Reported-by: Nicholas Carlini <npc@anthropic.com>
Suggested-by: Nicholas Carlini <npc@anthropic.com>
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://patch.msgid.link/20260917233222.2542500-7-memxor@gmail.com
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
parent
968ee7c06b
commit
394ae39833
|
|
@ -980,23 +980,30 @@ static int bpf_core_calc_relo(const char *prog_name,
|
|||
}
|
||||
|
||||
/*
|
||||
* Turn instruction for which CO_RE relocation failed into invalid one with
|
||||
* Turn instruction for which CO-RE relocation failed into invalid one with
|
||||
* distinct signature.
|
||||
*/
|
||||
static void bpf_core_poison_insn(const char *prog_name, int relo_idx,
|
||||
int insn_idx, struct bpf_insn *insn)
|
||||
static int bpf_core_poison_insn(const char *prog_name, int relo_idx,
|
||||
struct bpf_insn *insn, int insn_idx)
|
||||
{
|
||||
pr_debug("prog '%s': relo #%d: substituting insn #%d w/ invalid insn\n",
|
||||
prog_name, relo_idx, insn_idx);
|
||||
insn->code = BPF_JMP | BPF_CALL;
|
||||
insn->dst_reg = 0;
|
||||
insn->src_reg = 0;
|
||||
insn->off = 0;
|
||||
/* if this instruction is reachable (not a dead code),
|
||||
* verifier will complain with the following message:
|
||||
* invalid func unknown#195896080
|
||||
*/
|
||||
insn->imm = 195896080; /* => 0xbad2310 => "bad relo" */
|
||||
int insn_cnt = is_ldimm64_insn(insn) ? 2 : 1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < insn_cnt; i++) {
|
||||
pr_debug("prog '%s': relo #%d: substituting insn #%d w/ invalid insn\n",
|
||||
prog_name, relo_idx, insn_idx + i);
|
||||
insn[i].code = BPF_JMP | BPF_CALL;
|
||||
insn[i].dst_reg = 0;
|
||||
insn[i].src_reg = 0;
|
||||
insn[i].off = 0;
|
||||
/*
|
||||
* If this instruction is reachable (not dead code), the verifier
|
||||
* will complain with "invalid func unknown#195896080".
|
||||
*/
|
||||
insn[i].imm = 195896080; /* => 0xbad2310 => "bad relo" */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insn_bpf_size_to_bytes(struct bpf_insn *insn)
|
||||
|
|
@ -1047,17 +1054,6 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
|
||||
class = BPF_CLASS(insn->code);
|
||||
|
||||
if (res->poison) {
|
||||
poison:
|
||||
/* poison second part of ldimm64 to avoid confusing error from
|
||||
* verifier about "unknown opcode 00"
|
||||
*/
|
||||
if (is_ldimm64_insn(insn))
|
||||
bpf_core_poison_insn(prog_name, relo_idx, insn_idx + 1, insn + 1);
|
||||
bpf_core_poison_insn(prog_name, relo_idx, insn_idx, insn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
orig_val = res->orig_val;
|
||||
new_val = res->new_val;
|
||||
|
||||
|
|
@ -1065,7 +1061,9 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
case BPF_ALU:
|
||||
case BPF_ALU64:
|
||||
if (BPF_SRC(insn->code) != BPF_K)
|
||||
return -EINVAL;
|
||||
goto bad_insn;
|
||||
if (res->poison)
|
||||
return bpf_core_poison_insn(prog_name, relo_idx, insn, insn_idx);
|
||||
if (res->validate && insn->imm != orig_val) {
|
||||
pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %d, exp %llu -> %llu\n",
|
||||
prog_name, relo_idx,
|
||||
|
|
@ -1082,6 +1080,8 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
case BPF_LDX:
|
||||
case BPF_ST:
|
||||
case BPF_STX:
|
||||
if (res->poison)
|
||||
return bpf_core_poison_insn(prog_name, relo_idx, insn, insn_idx);
|
||||
if (res->validate && insn->off != orig_val) {
|
||||
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %d, exp %llu -> %llu\n",
|
||||
prog_name, relo_idx, insn_idx, insn->off, (unsigned long long)orig_val,
|
||||
|
|
@ -1097,7 +1097,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
pr_warn("prog '%s': relo #%d: insn #%d (LDX/ST/STX) accesses field incorrectly. "
|
||||
"Make sure you are accessing pointers, unsigned integers, or fields of matching type and size.\n",
|
||||
prog_name, relo_idx, insn_idx);
|
||||
goto poison;
|
||||
return bpf_core_poison_insn(prog_name, relo_idx, insn, insn_idx);
|
||||
}
|
||||
|
||||
orig_val = insn->off;
|
||||
|
|
@ -1140,6 +1140,9 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (res->poison)
|
||||
return bpf_core_poison_insn(prog_name, relo_idx, insn, insn_idx);
|
||||
|
||||
imm = (__u32)insn[0].imm | ((__u64)insn[1].imm << 32);
|
||||
if (res->validate && imm != orig_val) {
|
||||
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
|
||||
|
|
@ -1157,6 +1160,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
|
|||
break;
|
||||
}
|
||||
default:
|
||||
bad_insn:
|
||||
pr_warn("prog '%s': relo #%d: trying to relocate unrecognized insn #%d, code:0x%x, src:0x%x, dst:0x%x, off:0x%x, imm:0x%x\n",
|
||||
prog_name, relo_idx, insn_idx, insn->code,
|
||||
(unsigned)insn->src_reg, (unsigned)insn->dst_reg, (unsigned)insn->off, (unsigned)insn->imm);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user