On Wed, Oct 16, 2019 at 4:16 AM Alexei Starovoitov <ast@xxxxxxxxxx> wrote: > > libbpf analyzes bpf C program, searches in-kernel BTF for given type name > and stores it into expected_attach_type. > The kernel verifier expects this btf_id to point to something like: > typedef void (*btf_trace_kfree_skb)(void *, struct sk_buff *skb, void *loc); > which represents signature of raw_tracepoint "kfree_skb". > > Then btf_ctx_access() matches ctx+0 access in bpf program with 'skb' > and 'ctx+8' access with 'loc' arguments of "kfree_skb" tracepoint. > In first case it passes btf_id of 'struct sk_buff *' back to the verifier core > and 'void *' in second case. > > Then the verifier tracks PTR_TO_BTF_ID as any other pointer type. > Like PTR_TO_SOCKET points to 'struct bpf_sock', > PTR_TO_TCP_SOCK points to 'struct bpf_tcp_sock', and so on. > PTR_TO_BTF_ID points to in-kernel structs. > If 1234 is btf_id of 'struct sk_buff' in vmlinux's BTF > then PTR_TO_BTF_ID#1234 points to one of in kernel skbs. > > When PTR_TO_BTF_ID#1234 is dereferenced (like r2 = *(u64 *)r1 + 32) > the btf_struct_access() checks which field of 'struct sk_buff' is > at offset 32. Checks that size of access matches type definition > of the field and continues to track the dereferenced type. > If that field was a pointer to 'struct net_device' the r2's type > will be PTR_TO_BTF_ID#456. Where 456 is btf_id of 'struct net_device' > in vmlinux's BTF. > > Such verifier analysis prevents "cheating" in BPF C program. > The program cannot cast arbitrary pointer to 'struct sk_buff *' > and access it. C compiler would allow type cast, of course, > but the verifier will notice type mismatch based on BPF assembly > and in-kernel BTF. > > Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> > --- > include/linux/bpf.h | 17 +++- > include/linux/bpf_verifier.h | 4 + > kernel/bpf/btf.c | 190 +++++++++++++++++++++++++++++++++++ > kernel/bpf/verifier.c | 88 +++++++++++++++- > kernel/trace/bpf_trace.c | 2 +- > 5 files changed, 296 insertions(+), 5 deletions(-) > Maybe it's just me reading this code for Nth time, but I find btf_struct_access() much easier to follow now. Thanks! Acked-by: Andrii Nakryiko <andriin@xxxxxx> [...] > static void print_verifier_state(struct bpf_verifier_env *env, > const struct bpf_func_state *state) > { > @@ -460,6 +480,8 @@ static void print_verifier_state(struct bpf_verifier_env *env, > /* reg->off should be 0 for SCALAR_VALUE */ > verbose(env, "%lld", reg->var_off.value + reg->off); > } else { > + if (t == PTR_TO_BTF_ID) > + verbose(env, "%s", kernel_type_name(reg->btf_id)); > verbose(env, "(id=%d", reg->id); not related to specific changes in this patch set, just to bring this up, but this extra id=%d part is quite confusing for register types that shouldn't really have id associated with it. We should probably add some filter here to print this only for ref-tracked register types. > if (reg_type_may_be_refcounted_or_null(t)) > verbose(env, ",ref_obj_id=%d", reg->ref_obj_id); > @@ -2337,10 +2359,12 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, > > /* check access to 'struct bpf_context' fields. Supports fixed offsets only */ > static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, [...]