Bpf Verifier Rejects Code: "Invalid Bpf_Context Access"

BPF verifier rejects code: invalid bpf_context access

This is because your BPF program is a “socket filter”, and that such programs are not allowed to do direct packet access (see sk_filter_is_valid_access(), where we return false on trying to read skb->data or skb->data_end for example). I do not know the specific reason why it is not available, although I suspect this would be a security precaution as socket filter programs may be available to unprivileged users.

Your program loads just fine as a TC classifier, for example (bpftool prog load foo.o /sys/fs/bpf/foo type classifier -- By the way thanks for the standalone working reproducer, much appreciated!).

If you want to access data for a socket filter, you can still use the bpf_skb_load_bytes() (or bpf_skb_store_bytes()) helper, which automatically does the check on length. Something like this:

#include <linux/bpf.h>

#define SEC(NAME) __attribute__((section(NAME), used))

static void *(*bpf_skb_load_bytes)(const struct __sk_buff *, __u32,
void *, __u32) =
(void *) BPF_FUNC_skb_load_bytes;

SEC("socket_filter")
int myprog(struct __sk_buff *skb)
{
__u32 foo;

if (bpf_skb_load_bytes(skb, 0, &foo, sizeof(foo)))
return 0;
if (foo == 3)
return 0;
return 1;
}

Regarding your last comment:

However it only happens if I don't try to check the bounds later.

I suspect clang compiles out the assignments for data and data_end if you do not use them in your code, so they are no longer present and no longer a problem for the verifier.

BPF verifier rejects when try to access __sk_buff member

The error is not caused by bpf_trace_prink(), but by the skb accesses that are present in your bytecode only when you call bpf_trace_printk().

Accessing skb->local_ip4 and skb->remote_ip4 is not allowed for programs of types BPF_PROG_TYPE_LWT_OUT, that you use.

See kernel code: The function that checks for valid access for this type returns false for certain offsets or range in skb:

case bpf_ctx_range_till(struct __sk_buff, family, local_port):
[...]
return false;

This corresponds to the range where local_ip4 and remote_ip4 are defined:

struct __sk_buff {
[...]

/* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
__u32 family;
__u32 remote_ip4; /* Stored in network byte order */
__u32 local_ip4; /* Stored in network byte order */
__u32 remote_ip6[4]; /* Stored in network byte order */
__u32 local_ip6[4]; /* Stored in network byte order */
__u32 remote_port; /* Stored in network byte order */
__u32 local_port; /* stored in host byte order */
/* ... here. */

When you remove your call to the bpf_trace_printk() helper, your local variables are no longer needed and clang compiles your code out of the program. The attempt to read at forbidden offsets is no longer part of your bytecode, so the program loads successfully.

invalid bpf_context access when trying to read `regs` parameter

This is essentially a bug and has been fixed in Linux 5.15.78.

  • https://cdn.kernel.org/pub/linux/kernel/v5.x/ChangeLog-5.15.78

This is what the commit log says:

    With just the forward declaration of the 'struct pt_regs' in
syscall_wrapper.h, the syscall stub functions:

__[x64|ia32]_sys_*(struct pt_regs *regs)

will have different definition of 'regs' argument in BTF data
based on which object file they are defined in.

If the syscall's object includes 'struct pt_regs' definition,
the BTF argument data will point to a 'struct pt_regs' record,
like:

[226] STRUCT 'pt_regs' size=168 vlen=21
'r15' type_id=1 bits_offset=0
'r14' type_id=1 bits_offset=64
'r13' type_id=1 bits_offset=128
...

If not, it will point to a fwd declaration record:

[15439] FWD 'pt_regs' fwd_kind=struct

and make bpf tracing program hooking on those functions unable
to access fields from 'struct pt_regs'.

Include asm/ptrace.h directly in syscall_wrapper.h to make sure all
syscalls see 'struct pt_regs' definition. This then results in BTF for
'__*_sys_*(struct pt_regs *regs)' functions to point to the actual
struct, not just the forward declaration.

Replacing a forward declaration struct pt_regs; in asm/ptrace.h with an actual definition #include <asm/ptrace.h> fixes the issue.

No direct packet access in BPF program with just CAP_BPF?

To make direct packet accesses in your program, you will need CAP_PERFMON in addition to CAP_BPF. I'm not aware of any way around it.

Why?

Because of Spectre vulnerabilities, someone able to perform arithmetic on unbounded pointers (i.e., all except stack and map value pointers) can read arbitrary memory via speculative out-of-bounds loads.

Such operations thus need to be forbidden for unprivileged users. Allowing CAP_BPF users to perform those operations would essentially give read access to arbitrary memory to CAP_BPF. For those reasons, I doubt this limitation will be lifted in the future.

net/core/filter.c and linux/bpf/verifier.c

As far as I know, cBPF program are only used for seccomp-bpf and as socket filters.

  • For seccomp-bpf, the actual call is in kernel/seccomp.c, after the program has been converted to eBPF.
  • For socket filters, the actual call is in net/core/filter.c, as you guessed. sk_filter_trim_cap() then gets called in a number of places in the kernel, for all types of sockets.


Related Topics



Leave a reply



Submit