On Sat, 13 Jul 2024 at 18:45, andrea terzolo <andreaterzolo3@xxxxxxxxx> wrote: > > Hi folks! I would like to check with you if the verifier failure I'm > facing is expected. The verifier rejects the following eBPF program in > recent kernel versions (e.g. 6.8.0-1010-aws). The same program > correctly works on older kernel versions (e.g. 6.5.0-41-generic) so > it's unclear to me why now the verifier should reject it. > > ``` > #define EXE_PATH_MAX_LEN 512 > char exe_path[EXE_PATH_MAX_LEN]; > > SEC("iter.s/task") > int dump_task(struct bpf_iter__task *ctx) { > struct task_struct *task = ctx->task; > > if (task == NULL) { > return 0; > } > > struct file *exe_file = task->mm->exe_file; > if (exe_file != NULL) { > bpf_d_path(&(exe_file->f_path), exe_path, EXE_PATH_MAX_LEN); > bpf_printk("exe path: %s", exe_path); > } > return 0; > } > ``` > > Verifier log > > ``` > -- BEGIN PROG LOAD LOG -- > 0: R1=ctx() R10=fp0 > ; struct task_struct *task = ctx->task; > 0: (79) r1 = *(u64 *)(r1 +8) ; > R1_w=trusted_ptr_or_null_task_struct(id=1) > ; if (task == NULL) { > 1: (15) if r1 == 0x0 goto pc+15 ; R1_w=trusted_ptr_task_struct() > ; struct file *exe_file = task->mm->exe_file; > 2: (79) r1 = *(u64 *)(r1 +2264) ; R1_w=untrusted_ptr_mm_struct() > ; struct file *exe_file = task->mm->exe_file; > 3: (79) r1 = *(u64 *)(r1 +1176) ; R1_w=untrusted_ptr_file() > ; if (exe_file != NULL) { > 4: (15) if r1 == 0x0 goto pc+12 ; R1_w=untrusted_ptr_file() > 5: (b7) r2 = 152 ; R2_w=152 > 6: (0f) r1 += r2 ; > R1_w=untrusted_ptr_file(off=152) R2_w=152 > ; bpf_d_path(&(exe_file->f_path), exe_path, EXE_PATH_MAX_LEN); > 7: (18) r2 = 0xffffa839013ee000 ; > R2_w=map_value(map=bpf_iter.bss,ks=4,vs=528) > 9: (b7) r3 = 512 ; R3_w=512 > 10: (85) call bpf_d_path#147 > R1 type=untrusted_ptr_ expected=ptr_, trusted_ptr_, rcu_ptr_ > processed 10 insns (limit 1000000) max_states_per_insn 0 total_states > 0 peak_states 0 mark_read 0 > -- END PROG LOAD LOG -- > ``` > > From what I understand, as soon as we dereference the `struct > task_struct *task` pointer inside an RCU CS we obtain an untrusted > pointer (e.g. after `task->mm` we already have an untrusted pointer). > Sorry, here I meant "outside an RCU CS" instead of "inside". > > This causes the `bpf_d_path` helper to fail because it receives an > `untrusted_ptr_file`. In this case, I marked the eBPF program as > sleepable on purpose, to trigger the failure. If I use a not-sleepable > bpf iterator the `bpf_d_path` helper correctly receives a > `PTR_TO_BTF_ID` and everything works fine. > Ok, I checked the code, and the reason seems that all non-sleepable eBPF programs are always contained in an RCU CS. So the `in_rcu_cs` method always returns `true`. To emulate the same behavior in a sleepable program we need to explicitly add an RCU CS. ``` #define EXE_PATH_MAX_LEN 512 char exe_path[EXE_PATH_MAX_LEN]; extern void bpf_rcu_read_lock(void) __ksym; extern void bpf_rcu_read_unlock(void) __ksym; SEC("iter.s/task") int dump_task(struct bpf_iter__task *ctx) { struct task_struct *task = ctx->task; if (task == NULL) { return 0; } bpf_rcu_read_lock(); struct file *exe_file = task->mm->exe_file; if (exe_file != NULL) { bpf_d_path(&(exe_file->f_path), exe_path, EXE_PATH_MAX_LEN); bpf_printk("exe path: %s", exe_path); } bpf_rcu_read_unlock(); return 0; } ``` > > I'm wondering if this is the expected behavior and if so, how can I > continue to use the `bpf_d_path` helper inside this sleepable ebpf > program? Is there a way to obtain again a `PTR_TO_BTF_ID` for > `&(exe_file->f_path)` to satisfy the `bpf_d_path` signature? So what has changed is that now in sleepable progs we explicitly need to create RCU CS when we dereference trusted pointers if we want to preserve `PTR_TO_BTF_ID` pointers