Hi, Sorry for bumping up- I just want to know whether it is a bug or just an yet unsupported usecase. Thanks! On Wed, Dec 16, 2020 at 2:40 PM Gilad Reti <gilad.reti@xxxxxxxxx> wrote: > > Hello there, > > I am having an issue with passing bpf programs through the verifier. > > For a minimal example, I took andrii's examples from libbpf-bootstrap > (bootstrap.bpf.c) and added the following lines (to forcibly claim all > available registers in order to cause register spilling): > > int a, b, c, d, e_, f, g, h, i; > > a = b = c = d = e_ = f = g = h = i = 0; > asm volatile("" > : "=r"(a), "=r"(b), "=r"(c), "=r"(d), "=r"(e_), "=r"(f), > "=r"(g), "=r"(h), "=r"(i) > : "0"(a), "1"(b), "2"(c), "3"(d), "4"(e_), "5"(f), "6"(g), > "7"(h), "8"(i)); > > This causes r7 (the register pointing to the ringbuf reserved memory) > to spill out to the stack, and later when it is returned to the > registers it is marked as "inv" which causes the verifier to reject > loading the program. > > My setup is Linux 5.10.0, clang 11.0.0-2. > > For a reference, here is the complete bpf program (userspace program > is the same as andrii's): > > > // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause > /* Copyright (c) 2020 Facebook */ > #include "vmlinux.h" > #include <bpf/bpf_helpers.h> > #include <bpf/bpf_tracing.h> > #include <bpf/bpf_core_read.h> > #include "bootstrap.h" > > char LICENSE[] SEC("license") = "Dual BSD/GPL"; > > struct > { > __uint(type, BPF_MAP_TYPE_HASH); > __uint(max_entries, 8192); > __type(key, pid_t); > __type(value, u64); > } exec_start SEC(".maps"); > > struct > { > __uint(type, BPF_MAP_TYPE_RINGBUF); > __uint(max_entries, 256 * 1024); > } rb SEC(".maps"); > > const volatile unsigned long long min_duration_ns = 0; > > SEC("tp/sched/sched_process_exec") > int handle_exec(struct trace_event_raw_sched_process_exec *ctx) > { > struct task_struct *task; > unsigned fname_off; > struct event *e; > pid_t pid; > u64 ts; > > /* remember time exec() was executed for this PID */ > pid = bpf_get_current_pid_tgid() >> 32; > ts = bpf_ktime_get_ns(); > bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); > > /* don't emit exec events when minimum duration is specified */ > if (min_duration_ns) > return 0; > > /* reserve sample from BPF ringbuf */ > e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); > if (!e) > return 0; > > /* fill out the sample with data */ > task = (struct task_struct *)bpf_get_current_task(); > > e->exit_event = false; > e->pid = pid; > e->ppid = BPF_CORE_READ(task, real_parent, tgid); > bpf_get_current_comm(&e->comm, sizeof(e->comm)); > > int a, b, c, d, e_, f, g, h, i; > > a = b = c = d = e_ = f = g = h = i = 0; > asm volatile("" > : "=r"(a), "=r"(b), "=r"(c), "=r"(d), "=r"(e_), > "=r"(f), "=r"(g), "=r"(h), "=r"(i) > : "0"(a), "1"(b), "2"(c), "3"(d), "4"(e_), "5"(f), > "6"(g), "7"(h), "8"(i)); > > fname_off = ctx->__data_loc_filename & 0xFFFF; > bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx > + fname_off); > > /* successfully submit it to user-space for post-processing */ > bpf_ringbuf_submit(e, 0); > return 0; > } > > SEC("tp/sched/sched_process_exit") > int handle_exit(struct trace_event_raw_sched_process_template *ctx) > { > struct task_struct *task; > struct event *e; > pid_t pid, tid; > u64 id, ts, *start_ts, duration_ns = 0; > > /* get PID and TID of exiting thread/process */ > id = bpf_get_current_pid_tgid(); > pid = id >> 32; > tid = (u32)id; > > /* ignore thread exits */ > if (pid != tid) > return 0; > > /* if we recorded start of the process, calculate lifetime duration */ > start_ts = bpf_map_lookup_elem(&exec_start, &pid); > if (start_ts) > duration_ns = bpf_ktime_get_ns() - *start_ts; > else if (min_duration_ns) > return 0; > bpf_map_delete_elem(&exec_start, &pid); > > /* if process didn't live long enough, return early */ > if (min_duration_ns && duration_ns < min_duration_ns) > return 0; > > /* reserve sample from BPF ringbuf */ > e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); > if (!e) > return 0; > > /* fill out the sample with data */ > task = (struct task_struct *)bpf_get_current_task(); > > e->exit_event = true; > e->duration_ns = duration_ns; > e->pid = pid; > e->ppid = BPF_CORE_READ(task, real_parent, tgid); > e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; > bpf_get_current_comm(&e->comm, sizeof(e->comm)); > > /* send data to user-space for post-processing */ > bpf_ringbuf_submit(e, 0); > return 0; > } > > > > > And libbpf's output: > > libbpf: load bpf program failed: Permission denied > libbpf: -- BEGIN DUMP LOG --- > libbpf: > Unrecognized arg#0 type PTR > ; int handle_exec(struct trace_event_raw_sched_process_exec *ctx) > 0: (bf) r6 = r1 > ; pid = bpf_get_current_pid_tgid() >> 32; > 1: (85) call bpf_get_current_pid_tgid#14 > ; pid = bpf_get_current_pid_tgid() >> 32; > 2: (77) r0 >>= 32 > ; pid = bpf_get_current_pid_tgid() >> 32; > 3: (63) *(u32 *)(r10 -4) = r0 > ; ts = bpf_ktime_get_ns(); > 4: (85) call bpf_ktime_get_ns#5 > ; ts = bpf_ktime_get_ns(); > 5: (7b) *(u64 *)(r10 -16) = r0 > 6: (bf) r2 = r10 > ; > 7: (07) r2 += -4 > 8: (bf) r3 = r10 > 9: (07) r3 += -16 > ; bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); > 10: (18) r1 = 0xffff8bf45ddd1400 > 12: (b7) r4 = 0 > 13: (85) call bpf_map_update_elem#2 > ; if (min_duration_ns) > 14: (18) r1 = 0xffffa1b980644000 > 16: (79) r1 = *(u64 *)(r1 +0) > R0=inv(id=0) R1_w=map_value(id=0,off=0,ks=4,vs=8,imm=0) > R6=ctx(id=0,off=0,imm=0) R10=fp0 fp-8=mmmm???? fp-16=mmmmmmmm > ; if (min_duration_ns) > 17: (55) if r1 != 0x0 goto pc+60 > last_idx 17 first_idx 14 > regs=2 stack=0 before 16: (79) r1 = *(u64 *)(r1 +0) > 18: (b7) r8 = 0 > ; e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); > 19: (18) r1 = 0xffff8bf461b60600 > 21: (b7) r2 = 168 > 22: (b7) r3 = 0 > 23: (85) call bpf_ringbuf_reserve#131 > 24: (bf) r7 = r0 > ; if (!e) > 25: (15) if r7 == 0x0 goto pc+52 > R0=mem(id=0,ref_obj_id=2,off=0,imm=0) R6=ctx(id=0,off=0,imm=0) > R7_w=mem(id=0,ref_obj_id=2,off=0,imm=0) R8=inv0 R10=fp0 fp-8=mmmm???? > fp-16=mmmmmmmm refs=2 > ; task = (struct task_struct *)bpf_get_current_task(); > 26: (85) call bpf_get_current_task#35 > ; e->exit_event = false; > 27: (73) *(u8 *)(r7 +167) = r8 > R0_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) > R7_w=mem(id=0,ref_obj_id=2,off=0,imm=0) R8=inv0 R10=fp0 fp-8=mmmm???? > fp-16=mmmmmmmm refs=2 > ; e->pid = pid; > 28: (61) r1 = *(u32 *)(r10 -4) > ; e->pid = pid; > 29: (63) *(u32 *)(r7 +0) = r1 > R0_w=inv(id=0) R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; > 0xffffffff)) R6=ctx(id=0,off=0,imm=0) > R7_w=mem(id=0,ref_obj_id=2,off=0,imm=0) R8=inv0 R10=fp0 fp-8=mmmm???? > fp-16=mmmmmmmm refs=2 > 30: (b7) r1 = 2264 > 31: (0f) r0 += r1 > 32: (bf) r1 = r10 > ; > 33: (07) r1 += -32 > ; e->ppid = BPF_CORE_READ(task, real_parent, tgid); > 34: (b7) r2 = 8 > 35: (bf) r3 = r0 > 36: (85) call bpf_probe_read_kernel#113 > last_idx 36 first_idx 24 > regs=4 stack=0 before 35: (bf) r3 = r0 > regs=4 stack=0 before 34: (b7) r2 = 8 > 37: (b7) r1 = 2252 > 38: (79) r3 = *(u64 *)(r10 -32) > 39: (0f) r3 += r1 > 40: (bf) r1 = r10 > ; > 41: (07) r1 += -20 > ; e->ppid = BPF_CORE_READ(task, real_parent, tgid); > 42: (b7) r2 = 4 > 43: (85) call bpf_probe_read_kernel#113 > last_idx 43 first_idx 37 > regs=4 stack=0 before 42: (b7) r2 = 4 > ; e->ppid = BPF_CORE_READ(task, real_parent, tgid); > 44: (61) r1 = *(u32 *)(r10 -20) > ; e->ppid = BPF_CORE_READ(task, real_parent, tgid); > 45: (63) *(u32 *)(r7 +4) = r1 > R0_w=inv(id=0) R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; > 0xffffffff)) R6=ctx(id=0,off=0,imm=0) > R7=mem(id=0,ref_obj_id=2,off=0,imm=0) R8=inv0 R10=fp0 fp-8=mmmm???? > fp-16=mmmmmmmm fp-24=mmmm???? fp-32=mmmmmmmm refs=2 > ; bpf_get_current_comm(&e->comm, sizeof(e->comm)); > 46: (bf) r1 = r7 > 47: (07) r1 += 24 > ; bpf_get_current_comm(&e->comm, sizeof(e->comm)); > 48: (b7) r2 = 16 > 49: (85) call bpf_get_current_comm#16 > R0_w=inv(id=0) R1_w=mem(id=0,ref_obj_id=2,off=24,imm=0) R2_w=inv16 > R6=ctx(id=0,off=0,imm=0) R7=mem(id=0,ref_obj_id=2,off=0,imm=0) R8=inv0 > R10=fp0 fp-8=mmmm???? fp-16=mmmmmmmm fp-24=mmmm???? fp-32=mmmmmmmm > refs=2 > last_idx 49 first_idx 37 > regs=4 stack=0 before 48: (b7) r2 = 16 > ; asm volatile("" > 50: (b7) r1 = 0 > 51: (7b) *(u64 *)(r10 -40) = r1 > last_idx 51 first_idx 50 > regs=2 stack=0 before 50: (b7) r1 = 0 > 52: (b7) r1 = 0 > 53: (7b) *(u64 *)(r10 -48) = r1 > last_idx 53 first_idx 50 > regs=2 stack=0 before 52: (b7) r1 = 0 > 54: (b7) r1 = 0 > 55: (7b) *(u64 *)(r10 -56) = r1 > last_idx 55 first_idx 50 > regs=2 stack=0 before 54: (b7) r1 = 0 > 56: (b7) r4 = 0 > 57: (b7) r5 = 0 > 58: (b7) r0 = 0 > 59: (b7) r8 = 0 > 60: (b7) r9 = 0 > 61: (bf) r3 = r6 > 62: (b7) r6 = 0 > 63: (79) r2 = *(u64 *)(r10 -40) > 64: (79) r1 = *(u64 *)(r10 -48) > 65: (7b) *(u64 *)(r10 -64) = r7 > 66: (79) r7 = *(u64 *)(r10 -56) > ; fname_off = ctx->__data_loc_filename & 0xFFFF; > 67: (61) r1 = *(u32 *)(r3 +8) > ; bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + > fname_off); > 68: (57) r1 &= 65535 > 69: (0f) r3 += r1 > last_idx 69 first_idx 50 > regs=2 stack=0 before 68: (57) r1 &= 65535 > regs=2 stack=0 before 67: (61) r1 = *(u32 *)(r3 +8) > 70: (79) r6 = *(u64 *)(r10 -64) > ; bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + > fname_off); > 71: (bf) r1 = r6 > 72: (07) r1 += 40 > ; bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + > fname_off); > 73: (b7) r2 = 127 > 74: (85) call bpf_probe_read_str#45 > R1 type=inv expected=fp, pkt, pkt_meta, map_value, mem, rdonly_buf, rdwr_buf > processed 72 insns (limit 1000000) max_states_per_insn 0 total_states > 4 peak_states 4 mark_read 4 > > libbpf: -- END LOG -- > libbpf: failed to load program 'handle_exec' > libbpf: failed to load object 'bootstrap_bpf' > libbpf: failed to load BPF skeleton 'bootstrap_bpf': -4007 > Failed to load and verify BPF skeleton > > > > Thanks for your time, > > Gilad Reti