mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 11:41:29 -04:00
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:
@@ -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, "; ");
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user