On Mon, Nov 4, 2024 at 9:46 AM Mikko Ylinen <mikko.ylinen@xxxxxxxxxxxxxxx> wrote: > > Hi, > > I was experimenting with struct_ops for my use-case but the programs > would not load because of "program of this type cannot use helper > xyz" error. However, [1] suggests that the ones I tried *are* supported. > Is the doc outdated or my steps are simply wrong here? > > Andrii suggested to report the case here with reproduce steps so here > it goes. > > with: > > diff --git a/tools/testing/selftests/bpf/progs/dummy_st_ops_success.c b/tools/testing/selftests/bpf/progs/dummy_st_ops_success.c > index ec0c595d47af..c3ca873957f0 100644 > --- a/tools/testing/selftests/bpf/progs/dummy_st_ops_success.c > +++ b/tools/testing/selftests/bpf/progs/dummy_st_ops_success.c > @@ -39,6 +39,7 @@ int BPF_PROG(test_2, struct bpf_dummy_ops_state *state, int a1, unsigned short a > test_2_args[2] = a2; > test_2_args[3] = a3; > test_2_args[4] = a4; > + bpf_printk("struct_ops/test_2"); > return 0; > } > > and: > tools/testing/selftests/bpf/vmtest.sh -- ./test_progs -t dummy_st_ops/dummy_st_ops_attach > > I get: > > [build + VM boot cut out] > ./test_progs -t dummy_st_ops/dummy_st_ops_attach > [ 1.068102] bpf_testmod: loading out-of-tree module taints kernel. > [ 1.068733] bpf_testmod: module verification failed: signature and/or required key missing - tainting kernel > tester_init:PASS:tester_log_buf 0 nsec > process_subtest:PASS:obj_open_mem 0 nsec > process_subtest:PASS:specs_alloc 0 nsec > libbpf: prog 'test_2': BPF program load failed: Invalid argument > libbpf: prog 'test_2': -- BEGIN PROG LOAD LOG -- > 0: R1=ctx() R10=fp0 > ; int BPF_PROG(test_2, struct bpf_dummy_ops_state *state, int a1, unsigned short a2, @ dummy_st_ops_success.c:40 > 0: (79) r2 = *(u64 *)(r1 +0) > func 'test_2' arg0 has btf_id 83075 type STRUCT 'bpf_dummy_ops_state' > 1: R1=ctx() R2_w=trusted_ptr_bpf_dummy_ops_state() > ; test_2_args[0] = state->val; @ dummy_st_ops_success.c:43 > 1: (61) r2 = *(u32 *)(r2 +0) ; R2_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) > ; int BPF_PROG(test_2, struct bpf_dummy_ops_state *state, int a1, unsigned short a2, @ dummy_st_ops_success.c:40 > 2: (79) r3 = *(u64 *)(r1 +8) ; R1=ctx() R3_w=scalar() > 3: (79) r4 = *(u64 *)(r1 +16) ; R1=ctx() R4_w=scalar() > 4: (79) r5 = *(u64 *)(r1 +24) ; R1=ctx() R5_w=scalar() > 5: (79) r1 = *(u64 *)(r1 +32) ; R1_w=scalar() > ; test_2_args[0] = state->val; @ dummy_st_ops_success.c:43 > 6: (18) r0 = 0xffffb456400f6000 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) > ; test_2_args[4] = a4; @ dummy_st_ops_success.c:47 > 8: (7b) *(u64 *)(r0 +32) = r1 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) R1_w=scalar() > ; test_2_args[3] = a3; @ dummy_st_ops_success.c:46 > 9: (67) r5 <<= 56 ; R5_w=scalar(smax=0x7f00000000000000,umax=0xff00000000000000,smin32=0,smax32=umax32=0,var_off=(0x0; 0xff00000000000000)) > 10: (c7) r5 s>>= 56 ; R5_w=scalar(smin=smin32=-128,smax=smax32=127) > 11: (7b) *(u64 *)(r0 +24) = r5 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) R5_w=scalar(smin=smin32=-128,smax=smax32=127) > ; int BPF_PROG(test_2, struct bpf_dummy_ops_state *state, int a1, unsigned short a2, @ dummy_st_ops_success.c:40 > 12: (57) r4 &= 65535 ; R4_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xffff,var_off=(0x0; 0xffff)) > ; test_2_args[2] = a2; @ dummy_st_ops_success.c:45 > 13: (7b) *(u64 *)(r0 +16) = r4 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) R4_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=0xffff,var_off=(0x0; 0xffff)) > ; test_2_args[1] = a1; @ dummy_st_ops_success.c:44 > 14: (67) r3 <<= 32 ; R3_w=scalar(smax=0x7fffffff00000000,umax=0xffffffff00000000,smin32=0,smax32=umax32=0,var_off=(0x0; 0xffffffff00000000)) > 15: (c7) r3 s>>= 32 ; R3_w=scalar(smin=0xffffffff80000000,smax=0x7fffffff) > 16: (7b) *(u64 *)(r0 +8) = r3 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) R3_w=scalar(smin=0xffffffff80000000,smax=0x7fffffff) > ; test_2_args[0] = state->val; @ dummy_st_ops_success.c:43 > 17: (67) r2 <<= 32 ; R2_w=scalar(smax=0x7fffffff00000000,umax=0xffffffff00000000,smin32=0,smax32=umax32=0,var_off=(0x0; 0xffffffff00000000)) > 18: (c7) r2 s>>= 32 ; R2_w=scalar(smin=0xffffffff80000000,smax=0x7fffffff) > 19: (7b) *(u64 *)(r0 +0) = r2 ; R0_w=map_value(map=dummy_st.bss,ks=4,vs=40) R2_w=scalar(smin=0xffffffff80000000,smax=0x7fffffff) > ; bpf_printk("struct_ops/test_2"); @ dummy_st_ops_success.c:48 > 20: (18) r1 = 0xffff9481c114e5d8 ; R1_w=map_value(map=dummy_st.rodata,ks=4,vs=18) > 22: (b4) w2 = 18 ; R2_w=18 > 23: (85) call bpf_trace_printk#6 > program of this type cannot use helper bpf_trace_printk#6 > processed 22 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 > -- END PROG LOAD LOG -- > libbpf: prog 'test_2': failed to load: -22 > libbpf: failed to load object 'dummy_st_ops_success' > libbpf: failed to load BPF skeleton 'dummy_st_ops_success': -22 > test_dummy_st_ops_attach:FAIL:dummy_st_ops_load unexpected error: -22 > #84/1 dummy_st_ops/dummy_st_ops_attach:FAIL > #84 dummy_st_ops:FAIL > This is expected. Each struct_ops has its own .get_func_proto callback. This is a dummy struct_ops for testing. It doesn't allow calling any helpers. get_func_proto is not set: static const struct bpf_verifier_ops bpf_dummy_verifier_ops = { .is_valid_access = bpf_dummy_ops_is_valid_access, .btf_struct_access = bpf_dummy_ops_btf_struct_access, };