Merge branch 'bpf-remove-artificial-limitations-on-pointer-types-eligable-for-spilling'

Eduard Zingerman says:

====================
bpf: remove artificial limitations on pointer types eligible for spilling

Track spills for the following register types precisely:
- PTR_TO_TP_BUFFER
- PTR_TO_INSN
- CONST_PTR_TO_DYNPTR
---
====================

Link: https://patch.msgid.link/20260707-missing-spillable-types-v1-0-44a92121dc41@gmail.com
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Kumar Kartikeya Dwivedi
2026-07-08 03:34:17 +02:00
3 changed files with 75 additions and 31 deletions

View File

@@ -3304,34 +3304,6 @@ static int mark_chain_precision_batch(struct bpf_verifier_env *env,
return bpf_mark_chain_precision(env, starting_state, -1, NULL);
}
static bool is_spillable_regtype(enum bpf_reg_type type)
{
switch (base_type(type)) {
case PTR_TO_MAP_VALUE:
case PTR_TO_STACK:
case PTR_TO_CTX:
case PTR_TO_PACKET:
case PTR_TO_PACKET_META:
case PTR_TO_PACKET_END:
case PTR_TO_FLOW_KEYS:
case CONST_PTR_TO_MAP:
case PTR_TO_SOCKET:
case PTR_TO_SOCK_COMMON:
case PTR_TO_TCP_SOCK:
case PTR_TO_XDP_SOCK:
case PTR_TO_BTF_ID:
case PTR_TO_BUF:
case PTR_TO_MEM:
case PTR_TO_FUNC:
case PTR_TO_MAP_KEY:
case PTR_TO_ARENA:
return true;
default:
return false;
}
}
/* check if register is a constant scalar value */
static bool is_reg_const(struct bpf_reg_state *reg, bool subreg32)
{
@@ -3345,13 +3317,18 @@ static u64 reg_const_value(struct bpf_reg_state *reg, bool subreg32)
return subreg32 ? tnum_subreg(reg->var_off).value : reg->var_off.value;
}
static bool is_pointer_regtype(enum bpf_reg_type type)
{
return type != SCALAR_VALUE && type != NOT_INIT;
}
static bool __is_pointer_value(bool allow_ptr_leaks,
const struct bpf_reg_state *reg)
{
if (allow_ptr_leaks)
return false;
return reg->type != SCALAR_VALUE;
return is_pointer_regtype(reg->type);
}
static void clear_scalar_id(struct bpf_reg_state *reg)
@@ -3476,7 +3453,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
if (value_regno >= 0)
reg = &cur->regs[value_regno];
if (!env->bypass_spec_v4) {
bool sanitize = reg && is_spillable_regtype(reg->type);
bool sanitize = reg && is_pointer_regtype(reg->type);
for (i = 0; i < size; i++) {
u8 type = state->stack[spi].slot_type[(slot - i) %
@@ -3517,7 +3494,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
__mark_reg_known(tmp_reg, insn->imm);
tmp_reg->type = SCALAR_VALUE;
save_register_state(env, state, spi, tmp_reg, size);
} else if (reg && is_spillable_regtype(reg->type)) {
} else if (reg && is_pointer_regtype(reg->type)) {
/* register containing pointer is being spilled into stack */
if (size != BPF_REG_SIZE) {
verbose_linfo(env, insn_idx, "; ");

View File

@@ -384,6 +384,31 @@ jt0_%=: \
: __clobber_all);
}
/* check valid spill/fill, ptr to insn */
SEC("socket")
__success
__naked void spill_fill_ptr_to_insn(void)
{
asm volatile (
".pushsection .jumptables,\"\",@progbits;"
"jt0_%=:"
".quad ret0_%= - socket;"
".size jt0_%=, 8;"
".global jt0_%=;"
".popsection;"
"r0 = jt0_%= ll;"
"r0 = *(u64 *)(r0 + 0);"
"*(u64 *)(r10 - 8) = r0;"
"r0 = *(u64 *)(r10 - 8);"
".8byte %[gotox_r0];"
"ret0_%=:"
"r0 = 0;"
"exit;"
:
: __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0, 0))
: __clobber_all);
}
#endif /* __TARGET_ARCH_x86 || __TARGET_ARCH_arm64 || __TARGET_ARCH_powerpc*/
char _license[] SEC("license") = "GPL";

View File

@@ -1403,4 +1403,46 @@ __naked void partial_fill_from_cleaned_pointer_spill(void)
::: __clobber_all);
}
/* check valid spill/fill, ptr to tp buffer */
SEC("raw_tracepoint.w")
__success
__naked void spill_fill_ptr_to_tp_buffer(void)
{
asm volatile (
"r6 = *(u64*)(r1 + 0);" /* r6 is the writable tracepoint buffer */
"*(u64*)(r10 - 8) = r6;"
"r7 = *(u64*)(r10 - 8);"
"r0 = 0;"
"*(u64*)(r7 + 0) = r0;" /* should be able to write through the buffer */
"r0 = 0;"
"exit;"
::: __clobber_all);
}
__noinline int spill_fill_dynptr_subprog(struct bpf_dynptr *dptr)
{
long *p;
asm volatile ("*(u64 *)(r10 - 8) = %[dptr];" /* spill the CONST_PTR_TO_DYNPTR argument */
"%[dptr] = *(u64 *)(r10 - 8);"
: [dptr] "+r"(dptr) :: "memory");
p = bpf_dynptr_data(dptr, 0, sizeof(*p));
if (!p)
return 0;
return 0;
}
static char dptr_mem_buf[16];
/* check valid spill/fill, const ptr to dynptr */
SEC("socket")
__success
int spill_fill_const_ptr_to_dynptr(void)
{
struct bpf_dynptr ptr;
bpf_dynptr_from_mem(dptr_mem_buf, sizeof(dptr_mem_buf), 0, &ptr);
return spill_fill_dynptr_subprog(&ptr);
}
char _license[] SEC("license") = "GPL";