* Problems with pointer offsets on ARM32 @ 2020-09-11 14:56 Luka Oreskovic 2020-09-11 18:13 ` Andrii Nakryiko 0 siblings, 1 reply; 8+ messages in thread From: Luka Oreskovic @ 2020-09-11 14:56 UTC (permalink / raw) To: bpf; +Cc: Luka Perkov, Juraj Vijtiuk Greetings everyone, I have been testing various BPF programs on the ARM32 architecture and have encountered a strange error. When trying to run a simple program that prints out the arguments of the open syscall, I found some strange behaviour with the pointer offsets when accessing the arguments: The output of llvm-objdump differed from the verifier error dump log. Notice the differences in lines 0 and 1. Why is the bytecode being altered at runtime? I attached the program, the llvm-objdump result and the verifier dump below. Best wishes, Luka Oreskovic BPF program -------------------------------------------- #include "vmlinux.h" #include <bpf/bpf_helpers.h> SEC("tracepoint/syscalls/sys_enter_open") int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) { const char *arg1 = (const char *)ctx->args[0]; int arg2 = (int)ctx->args[1]; bpf_printk("Open arg 1: %s\n", arg1); bpf_printk("Open arg 2: %d\n", arg2); return 0; } char LICENSE[] SEC("license") = "GPL"; llvm-objdump of program -------------------------------------------- Disassembly of section tracepoint/syscalls/sys_enter_open: 0000000000000000 tracepoint__syscalls__sys_enter_open: ; int arg2 = (int)ctx->args[1]; 0: 79 16 18 00 00 00 00 00 r6 = *(u64 *)(r1 + 24) ; const char *arg1 = (const char *)ctx->args[0]; 1: 79 13 10 00 00 00 00 00 r3 = *(u64 *)(r1 + 16) 2: 18 01 00 00 20 31 3a 20 00 00 00 00 25 73 0a 00 r1 = 2941353058775328 ll ; bpf_printk("Open arg 1: %s\n", arg1); 4: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 5: 18 07 00 00 4f 70 65 6e 00 00 00 00 20 61 72 67 r7 = 7454127125170581583 ll 7: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 8: bf a1 00 00 00 00 00 00 r1 = r10 9: 07 01 00 00 f0 ff ff ff r1 += -16 10: b7 02 00 00 10 00 00 00 r2 = 16 11: 85 00 00 00 06 00 00 00 call 6 12: 18 01 00 00 20 32 3a 20 00 00 00 00 25 64 0a 00 r1 = 2924860384358944 ll ; bpf_printk("Open arg 2: %d\n", arg2); 14: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 15: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 16: bf a1 00 00 00 00 00 00 r1 = r10 17: 07 01 00 00 f0 ff ff ff r1 += -16 18: b7 02 00 00 10 00 00 00 r2 = 16 19: bf 63 00 00 00 00 00 00 r3 = r6 20: 85 00 00 00 06 00 00 00 call 6 ; return 0; 21: b7 00 00 00 00 00 00 00 r0 = 0 22: 95 00 00 00 00 00 00 00 exit verifier output when running program -------------------------------------------- libbpf: -- BEGIN DUMP LOG --- libbpf: Unrecognized arg#0 type PTR ; int arg2 = (int)ctx->args[1]; 0: (79) r6 = *(u64 *)(r1 +16) ; const char *arg1 = (const char *)ctx->args[0]; 1: (79) r3 = *(u64 *)(r1 +12) invalid bpf_context access off=12 size=8 processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 libbpf: -- END LOG -- ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-11 14:56 Problems with pointer offsets on ARM32 Luka Oreskovic @ 2020-09-11 18:13 ` Andrii Nakryiko 2020-09-14 7:55 ` Luka Oreskovic 0 siblings, 1 reply; 8+ messages in thread From: Andrii Nakryiko @ 2020-09-11 18:13 UTC (permalink / raw) To: Luka Oreskovic; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic <luka.oreskovic@sartura.hr> wrote: > > Greetings everyone, > > I have been testing various BPF programs on the ARM32 architecture and > have encountered a strange error. > > When trying to run a simple program that prints out the arguments of > the open syscall, > I found some strange behaviour with the pointer offsets when accessing > the arguments: > The output of llvm-objdump differed from the verifier error dump log. > Notice the differences in lines 0 and 1. Why is the bytecode being > altered at runtime? > > I attached the program, the llvm-objdump result and the verifier dump below. > > Best wishes, > Luka Oreskovic > > BPF program > -------------------------------------------- > #include "vmlinux.h" > #include <bpf/bpf_helpers.h> > > SEC("tracepoint/syscalls/sys_enter_open") > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > { > const char *arg1 = (const char *)ctx->args[0]; > int arg2 = (int)ctx->args[1]; > > bpf_printk("Open arg 1: %s\n", arg1); > bpf_printk("Open arg 2: %d\n", arg2); > > return 0; > } > > char LICENSE[] SEC("license") = "GPL"; > > > llvm-objdump of program > -------------------------------------------- > Disassembly of section tracepoint/syscalls/sys_enter_open: > > 0000000000000000 tracepoint__syscalls__sys_enter_open: > ; int arg2 = (int)ctx->args[1]; > 0: 79 16 18 00 00 00 00 00 r6 = *(u64 *)(r1 + 24) > ; const char *arg1 = (const char *)ctx->args[0]; > 1: 79 13 10 00 00 00 00 00 r3 = *(u64 *)(r1 + 16) > 2: 18 01 00 00 20 31 3a 20 00 00 00 00 25 73 0a 00 r1 = > 2941353058775328 ll > ; bpf_printk("Open arg 1: %s\n", arg1); > 4: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > 5: 18 07 00 00 4f 70 65 6e 00 00 00 00 20 61 72 67 r7 = > 7454127125170581583 ll > 7: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > 8: bf a1 00 00 00 00 00 00 r1 = r10 > 9: 07 01 00 00 f0 ff ff ff r1 += -16 > 10: b7 02 00 00 10 00 00 00 r2 = 16 > 11: 85 00 00 00 06 00 00 00 call 6 > 12: 18 01 00 00 20 32 3a 20 00 00 00 00 25 64 0a 00 r1 = > 2924860384358944 ll > ; bpf_printk("Open arg 2: %d\n", arg2); > 14: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > 15: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > 16: bf a1 00 00 00 00 00 00 r1 = r10 > 17: 07 01 00 00 f0 ff ff ff r1 += -16 > 18: b7 02 00 00 10 00 00 00 r2 = 16 > 19: bf 63 00 00 00 00 00 00 r3 = r6 > 20: 85 00 00 00 06 00 00 00 call 6 > ; return 0; > 21: b7 00 00 00 00 00 00 00 r0 = 0 > 22: 95 00 00 00 00 00 00 00 exit > > > verifier output when running program > -------------------------------------------- > libbpf: -- BEGIN DUMP LOG --- > libbpf: > Unrecognized arg#0 type PTR > ; int arg2 = (int)ctx->args[1]; > 0: (79) r6 = *(u64 *)(r1 +16) > ; const char *arg1 = (const char *)ctx->args[0]; > 1: (79) r3 = *(u64 *)(r1 +12) > invalid bpf_context access off=12 size=8 > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > peak_states 0 mark_read 0 > > libbpf: -- END LOG -- One suspect would be libbpf's CO-RE relocations. Can you send full debug libbpf logs, it will have a full log of what libbpf adjusted. Please also include the definition of struct trace_event_raw_sys_enter from your vmlinux.h, as well as commit that your kernel was built from (to check the original definition). ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-11 18:13 ` Andrii Nakryiko @ 2020-09-14 7:55 ` Luka Oreskovic 2020-09-14 17:49 ` Andrii Nakryiko 0 siblings, 1 reply; 8+ messages in thread From: Luka Oreskovic @ 2020-09-14 7:55 UTC (permalink / raw) To: Andrii Nakryiko; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote: > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > <luka.oreskovic@sartura.hr> wrote: > > > > Greetings everyone, > > > > I have been testing various BPF programs on the ARM32 architecture and > > have encountered a strange error. > > > > When trying to run a simple program that prints out the arguments of > > the open syscall, > > I found some strange behaviour with the pointer offsets when accessing > > the arguments: > > The output of llvm-objdump differed from the verifier error dump log. > > Notice the differences in lines 0 and 1. Why is the bytecode being > > altered at runtime? > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > Best wishes, > > Luka Oreskovic > > > > BPF program > > -------------------------------------------- > > #include "vmlinux.h" > > #include <bpf/bpf_helpers.h> > > > > SEC("tracepoint/syscalls/sys_enter_open") > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > { > > const char *arg1 = (const char *)ctx->args[0]; > > int arg2 = (int)ctx->args[1]; > > > > bpf_printk("Open arg 1: %s\n", arg1); > > bpf_printk("Open arg 2: %d\n", arg2); > > > > return 0; > > } > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > llvm-objdump of program > > -------------------------------------------- > > Disassembly of section tracepoint/syscalls/sys_enter_open: > > > > 0000000000000000 tracepoint__syscalls__sys_enter_open: > > ; int arg2 = (int)ctx->args[1]; > > 0: 79 16 18 00 00 00 00 00 r6 = *(u64 *)(r1 + 24) > > ; const char *arg1 = (const char *)ctx->args[0]; > > 1: 79 13 10 00 00 00 00 00 r3 = *(u64 *)(r1 + 16) > > 2: 18 01 00 00 20 31 3a 20 00 00 00 00 25 73 0a 00 r1 = > > 2941353058775328 ll > > ; bpf_printk("Open arg 1: %s\n", arg1); > > 4: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > 5: 18 07 00 00 4f 70 65 6e 00 00 00 00 20 61 72 67 r7 = > > 7454127125170581583 ll > > 7: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > 8: bf a1 00 00 00 00 00 00 r1 = r10 > > 9: 07 01 00 00 f0 ff ff ff r1 += -16 > > 10: b7 02 00 00 10 00 00 00 r2 = 16 > > 11: 85 00 00 00 06 00 00 00 call 6 > > 12: 18 01 00 00 20 32 3a 20 00 00 00 00 25 64 0a 00 r1 = > > 2924860384358944 ll > > ; bpf_printk("Open arg 2: %d\n", arg2); > > 14: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > 15: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > 16: bf a1 00 00 00 00 00 00 r1 = r10 > > 17: 07 01 00 00 f0 ff ff ff r1 += -16 > > 18: b7 02 00 00 10 00 00 00 r2 = 16 > > 19: bf 63 00 00 00 00 00 00 r3 = r6 > > 20: 85 00 00 00 06 00 00 00 call 6 > > ; return 0; > > 21: b7 00 00 00 00 00 00 00 r0 = 0 > > 22: 95 00 00 00 00 00 00 00 exit > > > > > > verifier output when running program > > -------------------------------------------- > > libbpf: -- BEGIN DUMP LOG --- > > libbpf: > > Unrecognized arg#0 type PTR > > ; int arg2 = (int)ctx->args[1]; > > 0: (79) r6 = *(u64 *)(r1 +16) > > ; const char *arg1 = (const char *)ctx->args[0]; > > 1: (79) r3 = *(u64 *)(r1 +12) > > invalid bpf_context access off=12 size=8 > > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > > peak_states 0 mark_read 0 > > > > libbpf: -- END LOG -- > > > One suspect would be libbpf's CO-RE relocations. Can you send full > debug libbpf logs, it will have a full log of what libbpf adjusted. > Please also include the definition of struct trace_event_raw_sys_enter > from your vmlinux.h, as well as commit that your kernel was built from > (to check the original definition). Here is the data you requested. I can see the reallocations done by BPF CO-RE, but I don't understand why they would have to be done in the first place since I am using the vmlinux.h that has been generated using the devices vmlinux. Even if it made sense to change the pointer offsets, they shouldn't break the program. Kernel commit -------------------------------------------- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/?h=v5.9-rc3 Struct definition from vmlinux.h -------------------------------------------- struct trace_event_raw_sys_enter { struct trace_entry ent; long int id; long unsigned int args[6]; char __data[0]; }; Libbpf debug output -------------------------------------------- ibbpf: loading object 'hello_bpf' from buffer libbpf: elf: section(3) tracepoint/syscalls/sys_enter_open, size 184, link 0, flags 6, type=1 libbpf: elf: found program 'tracepoint/syscalls/sys_enter_open' libbpf: elf: section(4) .rodata.str1.1, size 32, link 0, flags 32, type=1 libbpf: elf: skipping unrecognized data section(4) .rodata.str1.1 libbpf: elf: section(5) license, size 4, link 0, flags 3, type=1 libbpf: license of hello_bpf is GPL libbpf: elf: section(12) .BTF, size 986, link 0, flags 0, type=1 libbpf: elf: section(14) .BTF.ext, size 252, link 0, flags 0, type=1 libbpf: elf: section(21) .symtab, size 936, link 1, flags 0, type=2 libbpf: looking for externs among 39 symbols... libbpf: collected 0 externs total libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0 libbpf: sec 'tracepoint/syscalls/sys_enter_open': found 2 CO-RE relocations libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: kind <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) libbpf: CO-RE relocating [2] struct trace_event_raw_sys_enter: found target candidate [4639] struct trace_event_raw_sys_entr libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: matching candidate #0 [4639] struct trace_event_raw_sys_enter.a) libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: patched insn #0 (LDX/ST/STX) off 24 -> 16 libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: kind <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: matching candidate #0 [4639] struct trace_event_raw_sys_enter.a) libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: patched insn #1 (LDX/ST/STX) off 16 -> 12 libbpf: load bpf program failed: Permission denied libbpf: -- BEGIN DUMP LOG --- libbpf: Unrecognized arg#0 type PTR ; int arg2 = (int)ctx->args[1]; 0: (79) r6 = *(u64 *)(r1 +16) ; const char *arg1 = (const char *)ctx->args[0]; 1: (79) r3 = *(u64 *)(r1 +12) invalid bpf_context access off=12 size=8 processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 libbpf: -- END LOG -- libbpf: failed to load program 'tracepoint/syscalls/sys_enter_open' libbpf: failed to load object 'hello_bpf' libbpf: failed to load BPF skeleton 'hello_bpf': -4007 failed to load BPF object -4007 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-14 7:55 ` Luka Oreskovic @ 2020-09-14 17:49 ` Andrii Nakryiko 2020-09-15 7:26 ` Luka Oreskovic 0 siblings, 1 reply; 8+ messages in thread From: Andrii Nakryiko @ 2020-09-14 17:49 UTC (permalink / raw) To: Luka Oreskovic; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Mon, Sep 14, 2020 at 12:55 AM Luka Oreskovic <luka.oreskovic@sartura.hr> wrote: > > On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko > <andrii.nakryiko@gmail.com> wrote: > > > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > > <luka.oreskovic@sartura.hr> wrote: > > > > > > Greetings everyone, > > > > > > I have been testing various BPF programs on the ARM32 architecture and > > > have encountered a strange error. > > > > > > When trying to run a simple program that prints out the arguments of > > > the open syscall, > > > I found some strange behaviour with the pointer offsets when accessing > > > the arguments: > > > The output of llvm-objdump differed from the verifier error dump log. > > > Notice the differences in lines 0 and 1. Why is the bytecode being > > > altered at runtime? > > > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > > > Best wishes, > > > Luka Oreskovic > > > > > > BPF program > > > -------------------------------------------- > > > #include "vmlinux.h" > > > #include <bpf/bpf_helpers.h> > > > > > > SEC("tracepoint/syscalls/sys_enter_open") > > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > > { > > > const char *arg1 = (const char *)ctx->args[0]; > > > int arg2 = (int)ctx->args[1]; > > > > > > bpf_printk("Open arg 1: %s\n", arg1); > > > bpf_printk("Open arg 2: %d\n", arg2); > > > > > > return 0; > > > } > > > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > > > > llvm-objdump of program > > > -------------------------------------------- > > > Disassembly of section tracepoint/syscalls/sys_enter_open: > > > > > > 0000000000000000 tracepoint__syscalls__sys_enter_open: > > > ; int arg2 = (int)ctx->args[1]; > > > 0: 79 16 18 00 00 00 00 00 r6 = *(u64 *)(r1 + 24) > > > ; const char *arg1 = (const char *)ctx->args[0]; > > > 1: 79 13 10 00 00 00 00 00 r3 = *(u64 *)(r1 + 16) > > > 2: 18 01 00 00 20 31 3a 20 00 00 00 00 25 73 0a 00 r1 = > > > 2941353058775328 ll > > > ; bpf_printk("Open arg 1: %s\n", arg1); > > > 4: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > > 5: 18 07 00 00 4f 70 65 6e 00 00 00 00 20 61 72 67 r7 = > > > 7454127125170581583 ll > > > 7: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > > 8: bf a1 00 00 00 00 00 00 r1 = r10 > > > 9: 07 01 00 00 f0 ff ff ff r1 += -16 > > > 10: b7 02 00 00 10 00 00 00 r2 = 16 > > > 11: 85 00 00 00 06 00 00 00 call 6 > > > 12: 18 01 00 00 20 32 3a 20 00 00 00 00 25 64 0a 00 r1 = > > > 2924860384358944 ll > > > ; bpf_printk("Open arg 2: %d\n", arg2); > > > 14: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > > 15: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > > 16: bf a1 00 00 00 00 00 00 r1 = r10 > > > 17: 07 01 00 00 f0 ff ff ff r1 += -16 > > > 18: b7 02 00 00 10 00 00 00 r2 = 16 > > > 19: bf 63 00 00 00 00 00 00 r3 = r6 > > > 20: 85 00 00 00 06 00 00 00 call 6 > > > ; return 0; > > > 21: b7 00 00 00 00 00 00 00 r0 = 0 > > > 22: 95 00 00 00 00 00 00 00 exit > > > > > > > > > verifier output when running program > > > -------------------------------------------- > > > libbpf: -- BEGIN DUMP LOG --- > > > libbpf: > > > Unrecognized arg#0 type PTR > > > ; int arg2 = (int)ctx->args[1]; > > > 0: (79) r6 = *(u64 *)(r1 +16) > > > ; const char *arg1 = (const char *)ctx->args[0]; > > > 1: (79) r3 = *(u64 *)(r1 +12) > > > invalid bpf_context access off=12 size=8 > > > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > > > peak_states 0 mark_read 0 > > > > > > libbpf: -- END LOG -- > > > > > > One suspect would be libbpf's CO-RE relocations. Can you send full > > debug libbpf logs, it will have a full log of what libbpf adjusted. > > Please also include the definition of struct trace_event_raw_sys_enter > > from your vmlinux.h, as well as commit that your kernel was built from > > (to check the original definition). > > > Here is the data you requested. I can see the reallocations done by BPF CO-RE, > but I don't understand why they would have to be done in the first place > since I am using the vmlinux.h that has been generated using the > devices vmlinux. > Even if it made sense to change the pointer offsets, they shouldn't > break the program. Ok, there are few things at work here. I'll try to explain it and let's see how we can fix this. This is all due to 32-bit architecture, of course. 1. BPF "architecture" is strictly 64-bit one. This means that from the BPF program point of view pointers are 64-bit values, long is 64-bit as well. 2. look at struct trace_event_raw_sys_enter below: id and each element of args[] array are longs. What this means is the following: - from BPF side of view those are assumed to be 64-bit integers - but in reality in ARM32 kernel and in memory they are 32-bit integers. So the memory view of struct trace_event_raw_sys_enter differs quite a lot, depending in which context that struct definition is used. You can see that from the compiled BPF assembly output above and compare to the actual memory layout, taking into account that long is 4 bytes. For BPF args[0] is at 16 bytes, args[1] is at 24 bytes. But in reality it's at 12 byte offset for args[0] and 16 byte offset for args[1]. So what libbpf/CO-RE is doing is right, it actually relocates offset 16 to 12 and 24 to 16. So without CO-RE you'd be accessing completely wrong offsets and reading garbage. 3. But read size matters as well. When compiling your BPF program clang sees 8-byte read and emits corresponding BPF assembly instruction. While in reality it has to be 4-byte read. Then later in the verifier (see tp_prog_is_valid_access() in kernel/trace/bpf_trace.c) there is a very simple check that all offsets are multiples of access size, and 12 % 8 != 0, so the verifier complains. It's a bit verbose explanation, but I hope it describes the problem. Basically, longs in a mixed BPF/32-bit land are error prone (as well as pointers in kernel data structures, btw). Now, a short-term fix for your use case would be to copy/paste struct trace_event_raw_sys_enter and replace all longs with ints. That will fix access size and offsets. Longer-term and a more proper fix is probably for libbpf to be smarter when emitting vmlinux.h and replace 4-byte longs with ints, knowing that vmlinux.h is going to be used from always-64-bit BPF architecture. I'll think a bit more about the implications of this, and if there are no problems with that approach, I'll submit a change soon enough. Pointers will still be a problem, though, and I'm not sure how we can solve it yet. Ideally there would be some solution that would cause Clang to emit 4-byte reads for pointers. I wonder if anyone else has any ideas around that. As a separate thing, it might be a good idea to allow 4-byte aligned 8-byte reads in the verifier on 32-bit architectures, given they are allowed on such architectures. Though for you that would lead to garbage in the upper 4 bytes of BPF register, which is equally bad. > > > Kernel commit > -------------------------------------------- > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/?h=v5.9-rc3 > > > Struct definition from vmlinux.h > -------------------------------------------- > struct trace_event_raw_sys_enter { > struct trace_entry ent; > long int id; > long unsigned int args[6]; > char __data[0]; > }; > > [...] > libbpf: collected 0 externs total > libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0 > libbpf: sec 'tracepoint/syscalls/sys_enter_open': found 2 CO-RE relocations > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: kind > <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) these logs seem to be truncated, btw > libbpf: CO-RE relocating [2] struct trace_event_raw_sys_enter: found > target candidate [4639] struct trace_event_raw_sys_entr > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: matching > candidate #0 [4639] struct trace_event_raw_sys_enter.a) like here it actually should be something like: struct trace_event_raw_sys_enter.args[0] > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: patched > insn #0 (LDX/ST/STX) off 24 -> 16 > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: kind > <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: matching > candidate #0 [4639] struct trace_event_raw_sys_enter.a) > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: patched > insn #1 (LDX/ST/STX) off 16 -> 12 > libbpf: load bpf program failed: Permission denied > libbpf: -- BEGIN DUMP LOG --- > libbpf: > Unrecognized arg#0 type PTR > ; int arg2 = (int)ctx->args[1]; > 0: (79) r6 = *(u64 *)(r1 +16) > ; const char *arg1 = (const char *)ctx->args[0]; > 1: (79) r3 = *(u64 *)(r1 +12) > invalid bpf_context access off=12 size=8 > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > peak_states 0 mark_read 0 > > libbpf: -- END LOG -- > libbpf: failed to load program 'tracepoint/syscalls/sys_enter_open' > libbpf: failed to load object 'hello_bpf' > libbpf: failed to load BPF skeleton 'hello_bpf': -4007 > failed to load BPF object -4007 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-14 17:49 ` Andrii Nakryiko @ 2020-09-15 7:26 ` Luka Oreskovic 2020-09-30 23:09 ` Andrii Nakryiko 0 siblings, 1 reply; 8+ messages in thread From: Luka Oreskovic @ 2020-09-15 7:26 UTC (permalink / raw) To: Andrii Nakryiko; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Mon, Sep 14, 2020 at 7:49 PM Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote: > > On Mon, Sep 14, 2020 at 12:55 AM Luka Oreskovic > <luka.oreskovic@sartura.hr> wrote: > > > > On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko > > <andrii.nakryiko@gmail.com> wrote: > > > > > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > Greetings everyone, > > > > > > > > I have been testing various BPF programs on the ARM32 architecture and > > > > have encountered a strange error. > > > > > > > > When trying to run a simple program that prints out the arguments of > > > > the open syscall, > > > > I found some strange behaviour with the pointer offsets when accessing > > > > the arguments: > > > > The output of llvm-objdump differed from the verifier error dump log. > > > > Notice the differences in lines 0 and 1. Why is the bytecode being > > > > altered at runtime? > > > > > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > > > > > Best wishes, > > > > Luka Oreskovic > > > > > > > > BPF program > > > > -------------------------------------------- > > > > #include "vmlinux.h" > > > > #include <bpf/bpf_helpers.h> > > > > > > > > SEC("tracepoint/syscalls/sys_enter_open") > > > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > > > { > > > > const char *arg1 = (const char *)ctx->args[0]; > > > > int arg2 = (int)ctx->args[1]; > > > > > > > > bpf_printk("Open arg 1: %s\n", arg1); > > > > bpf_printk("Open arg 2: %d\n", arg2); > > > > > > > > return 0; > > > > } > > > > > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > > > > > > > llvm-objdump of program > > > > -------------------------------------------- > > > > Disassembly of section tracepoint/syscalls/sys_enter_open: > > > > > > > > 0000000000000000 tracepoint__syscalls__sys_enter_open: > > > > ; int arg2 = (int)ctx->args[1]; > > > > 0: 79 16 18 00 00 00 00 00 r6 = *(u64 *)(r1 + 24) > > > > ; const char *arg1 = (const char *)ctx->args[0]; > > > > 1: 79 13 10 00 00 00 00 00 r3 = *(u64 *)(r1 + 16) > > > > 2: 18 01 00 00 20 31 3a 20 00 00 00 00 25 73 0a 00 r1 = > > > > 2941353058775328 ll > > > > ; bpf_printk("Open arg 1: %s\n", arg1); > > > > 4: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > > > 5: 18 07 00 00 4f 70 65 6e 00 00 00 00 20 61 72 67 r7 = > > > > 7454127125170581583 ll > > > > 7: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > > > 8: bf a1 00 00 00 00 00 00 r1 = r10 > > > > 9: 07 01 00 00 f0 ff ff ff r1 += -16 > > > > 10: b7 02 00 00 10 00 00 00 r2 = 16 > > > > 11: 85 00 00 00 06 00 00 00 call 6 > > > > 12: 18 01 00 00 20 32 3a 20 00 00 00 00 25 64 0a 00 r1 = > > > > 2924860384358944 ll > > > > ; bpf_printk("Open arg 2: %d\n", arg2); > > > > 14: 7b 1a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r1 > > > > 15: 7b 7a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r7 > > > > 16: bf a1 00 00 00 00 00 00 r1 = r10 > > > > 17: 07 01 00 00 f0 ff ff ff r1 += -16 > > > > 18: b7 02 00 00 10 00 00 00 r2 = 16 > > > > 19: bf 63 00 00 00 00 00 00 r3 = r6 > > > > 20: 85 00 00 00 06 00 00 00 call 6 > > > > ; return 0; > > > > 21: b7 00 00 00 00 00 00 00 r0 = 0 > > > > 22: 95 00 00 00 00 00 00 00 exit > > > > > > > > > > > > verifier output when running program > > > > -------------------------------------------- > > > > libbpf: -- BEGIN DUMP LOG --- > > > > libbpf: > > > > Unrecognized arg#0 type PTR > > > > ; int arg2 = (int)ctx->args[1]; > > > > 0: (79) r6 = *(u64 *)(r1 +16) > > > > ; const char *arg1 = (const char *)ctx->args[0]; > > > > 1: (79) r3 = *(u64 *)(r1 +12) > > > > invalid bpf_context access off=12 size=8 > > > > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > > > > peak_states 0 mark_read 0 > > > > > > > > libbpf: -- END LOG -- > > > > > > > > > One suspect would be libbpf's CO-RE relocations. Can you send full > > > debug libbpf logs, it will have a full log of what libbpf adjusted. > > > Please also include the definition of struct trace_event_raw_sys_enter > > > from your vmlinux.h, as well as commit that your kernel was built from > > > (to check the original definition). > > > > > > Here is the data you requested. I can see the reallocations done by BPF CO-RE, > > but I don't understand why they would have to be done in the first place > > since I am using the vmlinux.h that has been generated using the > > devices vmlinux. > > Even if it made sense to change the pointer offsets, they shouldn't > > break the program. > > Ok, there are few things at work here. I'll try to explain it and > let's see how we can fix this. This is all due to 32-bit architecture, > of course. > > 1. BPF "architecture" is strictly 64-bit one. This means that from the > BPF program point of view pointers are 64-bit values, long is 64-bit > as well. > 2. look at struct trace_event_raw_sys_enter below: id and each element > of args[] array are longs. What this means is the following: > - from BPF side of view those are assumed to be 64-bit integers > - but in reality in ARM32 kernel and in memory they are 32-bit integers. > > So the memory view of struct trace_event_raw_sys_enter differs quite a > lot, depending in which context that struct definition is used. You > can see that from the compiled BPF assembly output above and compare > to the actual memory layout, taking into account that long is 4 bytes. > For BPF args[0] is at 16 bytes, args[1] is at 24 bytes. But in reality > it's at 12 byte offset for args[0] and 16 byte offset for args[1]. So > what libbpf/CO-RE is doing is right, it actually relocates offset 16 > to 12 and 24 to 16. So without CO-RE you'd be accessing completely > wrong offsets and reading garbage. > 3. But read size matters as well. When compiling your BPF program > clang sees 8-byte read and emits corresponding BPF assembly > instruction. While in reality it has to be 4-byte read. Then later in > the verifier (see tp_prog_is_valid_access() in > kernel/trace/bpf_trace.c) there is a very simple check that all > offsets are multiples of access size, and 12 % 8 != 0, so the verifier > complains. > > It's a bit verbose explanation, but I hope it describes the problem. > Basically, longs in a mixed BPF/32-bit land are error prone (as well > as pointers in kernel data structures, btw). > > Now, a short-term fix for your use case would be to copy/paste struct > trace_event_raw_sys_enter and replace all longs with ints. That will > fix access size and offsets. > > Longer-term and a more proper fix is probably for libbpf to be smarter > when emitting vmlinux.h and replace 4-byte longs with ints, knowing > that vmlinux.h is going to be used from always-64-bit BPF > architecture. I'll think a bit more about the implications of this, > and if there are no problems with that approach, I'll submit a change > soon enough. Pointers will still be a problem, though, and I'm not > sure how we can solve it yet. Ideally there would be some solution > that would cause Clang to emit 4-byte reads for pointers. I wonder if > anyone else has any ideas around that. > > As a separate thing, it might be a good idea to allow 4-byte aligned > 8-byte reads in the verifier on 32-bit architectures, given they are > allowed on such architectures. Though for you that would lead to > garbage in the upper 4 bytes of BPF register, which is equally bad. > > > > > > > Kernel commit > > -------------------------------------------- > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/?h=v5.9-rc3 > > > > > > Struct definition from vmlinux.h > > -------------------------------------------- > > struct trace_event_raw_sys_enter { > > struct trace_entry ent; > > long int id; > > long unsigned int args[6]; > > char __data[0]; > > }; > > > > > > [...] > > > libbpf: collected 0 externs total > > libbpf: loading kernel BTF '/sys/kernel/btf/vmlinux': 0 > > libbpf: sec 'tracepoint/syscalls/sys_enter_open': found 2 CO-RE relocations > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: kind > > <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) > > these logs seem to be truncated, btw > > > libbpf: CO-RE relocating [2] struct trace_event_raw_sys_enter: found > > target candidate [4639] struct trace_event_raw_sys_entr > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: matching > > candidate #0 [4639] struct trace_event_raw_sys_enter.a) > > like here it actually should be something like: > > struct trace_event_raw_sys_enter.args[0] > > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #0: patched > > insn #0 (LDX/ST/STX) off 24 -> 16 > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: kind > > <byte_off> (0), spec is [2] struct trace_event_raw_sys_ent) > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: matching > > candidate #0 [4639] struct trace_event_raw_sys_enter.a) > > libbpf: prog 'tracepoint/syscalls/sys_enter_open': relo #1: patched > > insn #1 (LDX/ST/STX) off 16 -> 12 > > libbpf: load bpf program failed: Permission denied > > libbpf: -- BEGIN DUMP LOG --- > > libbpf: > > Unrecognized arg#0 type PTR > > ; int arg2 = (int)ctx->args[1]; > > 0: (79) r6 = *(u64 *)(r1 +16) > > ; const char *arg1 = (const char *)ctx->args[0]; > > 1: (79) r3 = *(u64 *)(r1 +12) > > invalid bpf_context access off=12 size=8 > > processed 2 insns (limit 1000000) max_states_per_insn 0 total_states 0 > > peak_states 0 mark_read 0 > > > > libbpf: -- END LOG -- > > libbpf: failed to load program 'tracepoint/syscalls/sys_enter_open' > > libbpf: failed to load object 'hello_bpf' > > libbpf: failed to load BPF skeleton 'hello_bpf': -4007 > > failed to load BPF object -4007 Thank you very much for your informative reply. I look forward to the changes. Best wishes, Luka Oreskovic ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-15 7:26 ` Luka Oreskovic @ 2020-09-30 23:09 ` Andrii Nakryiko 2020-10-01 13:40 ` Luka Oreskovic 0 siblings, 1 reply; 8+ messages in thread From: Andrii Nakryiko @ 2020-09-30 23:09 UTC (permalink / raw) To: Luka Oreskovic; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Tue, Sep 15, 2020 at 12:26 AM Luka Oreskovic <luka.oreskovic@sartura.hr> wrote: > > On Mon, Sep 14, 2020 at 7:49 PM Andrii Nakryiko > <andrii.nakryiko@gmail.com> wrote: > > > > On Mon, Sep 14, 2020 at 12:55 AM Luka Oreskovic > > <luka.oreskovic@sartura.hr> wrote: > > > > > > On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko > > > <andrii.nakryiko@gmail.com> wrote: > > > > > > > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > > > Greetings everyone, > > > > > > > > > > I have been testing various BPF programs on the ARM32 architecture and > > > > > have encountered a strange error. > > > > > > > > > > When trying to run a simple program that prints out the arguments of > > > > > the open syscall, > > > > > I found some strange behaviour with the pointer offsets when accessing > > > > > the arguments: > > > > > The output of llvm-objdump differed from the verifier error dump log. > > > > > Notice the differences in lines 0 and 1. Why is the bytecode being > > > > > altered at runtime? > > > > > > > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > > > > > > > Best wishes, > > > > > Luka Oreskovic > > > > > > > > > > BPF program > > > > > -------------------------------------------- > > > > > #include "vmlinux.h" > > > > > #include <bpf/bpf_helpers.h> > > > > > > > > > > SEC("tracepoint/syscalls/sys_enter_open") > > > > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > > > > { > > > > > const char *arg1 = (const char *)ctx->args[0]; > > > > > int arg2 = (int)ctx->args[1]; Luka, can you apply the changes below to bpf_core_read.h header and read these args using BPF_CORE_READ() macro: const char *arg1 = (const char *)BPF_CORE_READ(ctx, args[0]); int arg2 = BPF_CORE_READ(ctx, args[1]); I'm curious if that will work (unfortunately I don't have a complete enough setup to test this). The patch is as follows (with broken tab<->space conversion, so please make changes by hand): diff --git a/tools/lib/bpf/bpf_core_read.h b/tools/lib/bpf/bpf_core_read.h index bbcefb3ff5a5..fee6328d36c0 100644 --- a/tools/lib/bpf/bpf_core_read.h +++ b/tools/lib/bpf/bpf_core_read.h @@ -261,14 +261,16 @@ enum bpf_enum_value_kind { #define ___type(...) typeof(___arrow(__VA_ARGS__)) #define ___read(read_fn, dst, src_type, src, accessor) \ - read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) + read_fn((void *)(dst), \ + bpf_core_field_size(((src_type)(src))->accessor), \ + &((src_type)(src))->accessor) /* "recursively" read a sequence of inner pointers using local __t var */ #define ___rd_first(src, a) ___read(bpf_core_read, &__t, ___type(src), src, a); #define ___rd_last(...) \ ___read(bpf_core_read, &__t, \ ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); -#define ___rd_p1(...) const void *__t; ___rd_first(__VA_ARGS__) +#define ___rd_p1(...) const void *__t = (void *)0; ___rd_first(__VA_ARGS__) #define ___rd_p2(...) ___rd_p1(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) #define ___rd_p3(...) ___rd_p2(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) #define ___rd_p4(...) ___rd_p3(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) BTW, this approach should work for reading pointers as well, it would be nice if you can test that as well. E.g., something like the following: struct task_struct *t = (void *)bpf_get_current_task(); int ppid = BPF_CORE_READ(t, group_leader, tgid); If you try it without the patch above, it should either read garbage or zero, but not a valid parent PID (please verify that as well). I really appreciate your help with testing, thanks! > > > > > > > > > > bpf_printk("Open arg 1: %s\n", arg1); > > > > > bpf_printk("Open arg 2: %d\n", arg2); > > > > > > > > > > return 0; > > > > > } > > > > > > > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > > > > > [...] > > Best wishes, > Luka Oreskovic ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-09-30 23:09 ` Andrii Nakryiko @ 2020-10-01 13:40 ` Luka Oreskovic 2020-10-01 18:21 ` Andrii Nakryiko 0 siblings, 1 reply; 8+ messages in thread From: Luka Oreskovic @ 2020-10-01 13:40 UTC (permalink / raw) To: Andrii Nakryiko; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Thu, Oct 1, 2020 at 1:09 AM Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote: > > On Tue, Sep 15, 2020 at 12:26 AM Luka Oreskovic > <luka.oreskovic@sartura.hr> wrote: > > > > On Mon, Sep 14, 2020 at 7:49 PM Andrii Nakryiko > > <andrii.nakryiko@gmail.com> wrote: > > > > > > On Mon, Sep 14, 2020 at 12:55 AM Luka Oreskovic > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko > > > > <andrii.nakryiko@gmail.com> wrote: > > > > > > > > > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > > > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > > > > > Greetings everyone, > > > > > > > > > > > > I have been testing various BPF programs on the ARM32 architecture and > > > > > > have encountered a strange error. > > > > > > > > > > > > When trying to run a simple program that prints out the arguments of > > > > > > the open syscall, > > > > > > I found some strange behaviour with the pointer offsets when accessing > > > > > > the arguments: > > > > > > The output of llvm-objdump differed from the verifier error dump log. > > > > > > Notice the differences in lines 0 and 1. Why is the bytecode being > > > > > > altered at runtime? > > > > > > > > > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > > > > > > > > > Best wishes, > > > > > > Luka Oreskovic > > > > > > > > > > > > BPF program > > > > > > -------------------------------------------- > > > > > > #include "vmlinux.h" > > > > > > #include <bpf/bpf_helpers.h> > > > > > > > > > > > > SEC("tracepoint/syscalls/sys_enter_open") > > > > > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > > > > > { > > > > > > const char *arg1 = (const char *)ctx->args[0]; > > > > > > int arg2 = (int)ctx->args[1]; > > Luka, can you apply the changes below to bpf_core_read.h header and > read these args using BPF_CORE_READ() macro: > > const char *arg1 = (const char *)BPF_CORE_READ(ctx, args[0]); > int arg2 = BPF_CORE_READ(ctx, args[1]); > > I'm curious if that will work (unfortunately I don't have a complete > enough setup to test this). > > The patch is as follows (with broken tab<->space conversion, so please > make changes by hand): > > diff --git a/tools/lib/bpf/bpf_core_read.h b/tools/lib/bpf/bpf_core_read.h > index bbcefb3ff5a5..fee6328d36c0 100644 > --- a/tools/lib/bpf/bpf_core_read.h > +++ b/tools/lib/bpf/bpf_core_read.h > @@ -261,14 +261,16 @@ enum bpf_enum_value_kind { > #define ___type(...) typeof(___arrow(__VA_ARGS__)) > > #define ___read(read_fn, dst, src_type, src, accessor) \ > - read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) > + read_fn((void *)(dst), \ > + bpf_core_field_size(((src_type)(src))->accessor), \ > + &((src_type)(src))->accessor) > > /* "recursively" read a sequence of inner pointers using local __t var */ > #define ___rd_first(src, a) ___read(bpf_core_read, &__t, ___type(src), src, a); > #define ___rd_last(...) > \ > ___read(bpf_core_read, &__t, \ > ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); > -#define ___rd_p1(...) const void *__t; ___rd_first(__VA_ARGS__) > +#define ___rd_p1(...) const void *__t = (void *)0; ___rd_first(__VA_ARGS__) > #define ___rd_p2(...) ___rd_p1(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > #define ___rd_p3(...) ___rd_p2(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > #define ___rd_p4(...) ___rd_p3(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > > > > BTW, this approach should work for reading pointers as well, it would > be nice if you can test that as well. E.g., something like the > following: > > struct task_struct *t = (void *)bpf_get_current_task(); > int ppid = BPF_CORE_READ(t, group_leader, tgid); > > If you try it without the patch above, it should either read garbage > or zero, but not a valid parent PID (please verify that as well). > > I really appreciate your help with testing, thanks! > > > > > > > > > > > > > > bpf_printk("Open arg 1: %s\n", arg1); > > > > > > bpf_printk("Open arg 2: %d\n", arg2); > > > > > > > > > > > > return 0; > > > > > > } > > > > > > > > > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > > > > > > > > > [...] > > > > > Best wishes, > > Luka Oreskovic Greetings, I have tested your patch using the BPF_CORE_READ() macro and everything works great! As for the pointer read, it prints valid PIDs both with the patch and without it. Thank you very much for your help. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: Problems with pointer offsets on ARM32 2020-10-01 13:40 ` Luka Oreskovic @ 2020-10-01 18:21 ` Andrii Nakryiko 0 siblings, 0 replies; 8+ messages in thread From: Andrii Nakryiko @ 2020-10-01 18:21 UTC (permalink / raw) To: Luka Oreskovic; +Cc: bpf, Luka Perkov, Juraj Vijtiuk On Thu, Oct 1, 2020 at 6:40 AM Luka Oreskovic <luka.oreskovic@sartura.hr> wrote: > > On Thu, Oct 1, 2020 at 1:09 AM Andrii Nakryiko > <andrii.nakryiko@gmail.com> wrote: > > > > On Tue, Sep 15, 2020 at 12:26 AM Luka Oreskovic > > <luka.oreskovic@sartura.hr> wrote: > > > > > > On Mon, Sep 14, 2020 at 7:49 PM Andrii Nakryiko > > > <andrii.nakryiko@gmail.com> wrote: > > > > > > > > On Mon, Sep 14, 2020 at 12:55 AM Luka Oreskovic > > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > > > On Fri, Sep 11, 2020 at 8:14 PM Andrii Nakryiko > > > > > <andrii.nakryiko@gmail.com> wrote: > > > > > > > > > > > > On Fri, Sep 11, 2020 at 9:45 AM Luka Oreskovic > > > > > > <luka.oreskovic@sartura.hr> wrote: > > > > > > > > > > > > > > Greetings everyone, > > > > > > > > > > > > > > I have been testing various BPF programs on the ARM32 architecture and > > > > > > > have encountered a strange error. > > > > > > > > > > > > > > When trying to run a simple program that prints out the arguments of > > > > > > > the open syscall, > > > > > > > I found some strange behaviour with the pointer offsets when accessing > > > > > > > the arguments: > > > > > > > The output of llvm-objdump differed from the verifier error dump log. > > > > > > > Notice the differences in lines 0 and 1. Why is the bytecode being > > > > > > > altered at runtime? > > > > > > > > > > > > > > I attached the program, the llvm-objdump result and the verifier dump below. > > > > > > > > > > > > > > Best wishes, > > > > > > > Luka Oreskovic > > > > > > > > > > > > > > BPF program > > > > > > > -------------------------------------------- > > > > > > > #include "vmlinux.h" > > > > > > > #include <bpf/bpf_helpers.h> > > > > > > > > > > > > > > SEC("tracepoint/syscalls/sys_enter_open") > > > > > > > int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx) > > > > > > > { > > > > > > > const char *arg1 = (const char *)ctx->args[0]; > > > > > > > int arg2 = (int)ctx->args[1]; > > > > Luka, can you apply the changes below to bpf_core_read.h header and > > read these args using BPF_CORE_READ() macro: > > > > const char *arg1 = (const char *)BPF_CORE_READ(ctx, args[0]); > > int arg2 = BPF_CORE_READ(ctx, args[1]); > > > > I'm curious if that will work (unfortunately I don't have a complete > > enough setup to test this). > > > > The patch is as follows (with broken tab<->space conversion, so please > > make changes by hand): > > > > diff --git a/tools/lib/bpf/bpf_core_read.h b/tools/lib/bpf/bpf_core_read.h > > index bbcefb3ff5a5..fee6328d36c0 100644 > > --- a/tools/lib/bpf/bpf_core_read.h > > +++ b/tools/lib/bpf/bpf_core_read.h > > @@ -261,14 +261,16 @@ enum bpf_enum_value_kind { > > #define ___type(...) typeof(___arrow(__VA_ARGS__)) > > > > #define ___read(read_fn, dst, src_type, src, accessor) \ > > - read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) > > + read_fn((void *)(dst), \ > > + bpf_core_field_size(((src_type)(src))->accessor), \ > > + &((src_type)(src))->accessor) > > > > /* "recursively" read a sequence of inner pointers using local __t var */ > > #define ___rd_first(src, a) ___read(bpf_core_read, &__t, ___type(src), src, a); > > #define ___rd_last(...) > > \ > > ___read(bpf_core_read, &__t, \ > > ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__)); > > -#define ___rd_p1(...) const void *__t; ___rd_first(__VA_ARGS__) > > +#define ___rd_p1(...) const void *__t = (void *)0; ___rd_first(__VA_ARGS__) > > #define ___rd_p2(...) ___rd_p1(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > > #define ___rd_p3(...) ___rd_p2(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > > #define ___rd_p4(...) ___rd_p3(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__) > > > > > > > > BTW, this approach should work for reading pointers as well, it would > > be nice if you can test that as well. E.g., something like the > > following: > > > > struct task_struct *t = (void *)bpf_get_current_task(); > > int ppid = BPF_CORE_READ(t, group_leader, tgid); > > > > If you try it without the patch above, it should either read garbage > > or zero, but not a valid parent PID (please verify that as well). > > > > I really appreciate your help with testing, thanks! > > > > > > > > > > > > > > > > > > bpf_printk("Open arg 1: %s\n", arg1); > > > > > > > bpf_printk("Open arg 2: %d\n", arg2); > > > > > > > > > > > > > > return 0; > > > > > > > } > > > > > > > > > > > > > > char LICENSE[] SEC("license") = "GPL"; > > > > > > > > > > > > > > > > > > [...] > > > > > > > > Best wishes, > > > Luka Oreskovic > > Greetings, > > I have tested your patch using the BPF_CORE_READ() macro and > everything works great! Ok, glad it worked. It needs to be a bit more work to be applicable in wide range of situation, but you can use it as a work around for now, at least. > > As for the pointer read, it prints valid PIDs both with the patch and > without it. That surprised me initially, but then I realized that 32-bit kernel will just chop off high 32 bits of BPF's 64-bit register when the helper is getting passed a pointer, so even if those high 32-bits have garbage (as should be the case here), it still works magically. > > Thank you very much for your help. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2020-10-01 18:21 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-09-11 14:56 Problems with pointer offsets on ARM32 Luka Oreskovic 2020-09-11 18:13 ` Andrii Nakryiko 2020-09-14 7:55 ` Luka Oreskovic 2020-09-14 17:49 ` Andrii Nakryiko 2020-09-15 7:26 ` Luka Oreskovic 2020-09-30 23:09 ` Andrii Nakryiko 2020-10-01 13:40 ` Luka Oreskovic 2020-10-01 18:21 ` Andrii Nakryiko
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.