Re: [RFC PATCH bpf-next v1 1/2] bpf: Explore PTR_TO_STACK as R0 for bpf_dynptr_slice_rdwr

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 19 Feb 2025 at 18:41, Alexei Starovoitov
<alexei.starovoitov@xxxxxxxxx> wrote:
>
> On Wed, Feb 19, 2025 at 4:51 AM Kumar Kartikeya Dwivedi
> <memxor@xxxxxxxxx> wrote:
> >
> > For the bpf_dynptr_slice_rdwr kfunc, the verifier may return a pointer
> > to the underlying packet (if the requested slice is linear), or copy out
> > the data to the buffer passed into the kfunc. The verifier performs
> > symbolic execution assuming the returned value is a PTR_TO_MEM of a
> > certain size (passed into the kfunc), and ensures reads and writes are
> > within bounds.
>
> sounds like
> check_kfunc_mem_size_reg() -> check_mem_size_reg() ->
> check_helper_mem_access()
>    case PTR_TO_STACK:
>       check_stack_range_initialized()
>          clobber = true
>              if (clobber) {
>                   __mark_reg_unknown(env, &state->stack[spi].spilled_ptr);
>
> is somehow broken?
>
> ohh. It might be:
> || !is_kfunc_arg_optional(meta->btf, buff_arg)
>
> This bit is wrong then.
> When arg is not-null check_kfunc_mem_size_reg() should be called.
> The PTR_TO_STACK abuse is a small subset of issues
> if check_kfunc_mem_size_reg() is indeed not called.

The condition looks ok to me.

The condition to do check_mem_size_reg is !null || !opt.
So when it's null, and it's opt, it will be skipped.
When it's null, and it's not opt, the check will happen.
When arg is not-null, the said function is called, opt does not matter then.
So the stack slots are marked misc.

In our case we're not passing a NULL pointer in the selftest.

The problem occurs once we spill to that slot _after_ the call, and
then do a write through returned mem pointer.

The final few lines from the selftest do the dirty thing, where r0 is
aliasing fp-8, and r1 = 0.

+ *(u64 *)(r10 - 8) = r8; \
+ *(u64 *)(r0 + 0) = r1; \
+ r8 = *(u64 *)(r10 - 8); \
+ r0 = *(u64 *)(r8 + 0); \

The write through r0 must re-mark the stack, but the verifier doesn't
know it's pointing to the stack.
push_stack was the conceptually cleaner/simpler fix, but it apparently
isn't good enough.

Remarking the stack on write to PTR_TO_MEM, or invalidating PTR_TO_MEM
when r8 is spilled to fp-8 first time after the call are two options.
Both have some concerns (first, the misaligned stack access is not
caught, second PTR_TO_MEM may outlive stack frame).

I don't recall if there was a hardware/JIT specific reason to care
about stack access alignment or not (on some architectures), but
otherwise we can over approximately mark at 8-byte granularity for any
slot(s) that overlap with the buffer to cover such a case. The second
problem is slightly trickier, which makes me lean towards invalidating
returned PTR_TO_MEM when stack slot is overwritten or frame is
destroyed.





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux