On Thu, Aug 29, 2024 at 2:38 AM Kees Cook <kees@xxxxxxxxxx> wrote: > On Tue, Aug 27, 2024 at 09:09:49PM -0700, Juefei Pu wrote: > > Hello, > > We found the following null-pointer-dereference issue using syzkaller > > on Linux v6.10. > > In seccomp! Yikes. > > > Unfortunately, the syzkaller failed to generate a reproducer. > > That's a bummer. > > > But at least we have the report: > > > > Oops: general protection fault, probably for non-canonical address > > 0xdffffc0000000006: 0000 [#1] PREEMPT SMP KASAN PTI > > KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] > > CPU: 0 PID: 4493 Comm: systemd-journal Not tainted 6.10.0 #13 > > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 > > RIP: 0010:__bpf_prog_run include/linux/filter.h:691 [inline] > > This doesn't look like a NULL deref, this looks like a corrupted > pointer: 0xdffffc0000000006. No, it really is a NULL deref - in a non-KASAN build, you'd see a page fault at virtual address 0x30. KASAN builds with inline instrumentation cause a GPF on NULL deref because they try to first check the KASAN shadow mapping for that address, and applying the shadow address calculation to NULL (or addresses in the low address space half) gives non-canonical addresses. This line directly below the oops message is supposed to point this out (it works by decoding the faulting instruction, calculating the effective address of the access, and then having KASAN calculate backwards from the shadow address what the original address could have been): KASAN: null-ptr-deref in range [0x0000000000000030-0x0000000000000037] > Is prog bad or dfunc bad? I assume the > former, as dfunc is hard-coded below... > > ret = dfunc(ctx, prog->insnsi, prog->bpf_func); > > > RIP: 0010:bpf_prog_run include/linux/filter.h:698 [inline] > > return __bpf_prog_run(prog, ctx, bpf_dispatcher_nop_func); > > > RIP: 0010:bpf_prog_run_pin_on_cpu include/linux/filter.h:715 [inline] > > ret = bpf_prog_run(prog, ctx); > > > RIP: 0010:seccomp_run_filters+0x17a/0x3f0 kernel/seccomp.c:426 > > u32 cur_ret = bpf_prog_run_pin_on_cpu(f->prog, sd); > > > Code: 00 00 e8 99 36 d2 ff 0f 1f 44 00 00 e8 cf 58 ff ff 48 8d 5d 48 > > 48 83 c5 30 48 89 e8 48 c1 e8 03 48 b9 00 00 00 00 00 fc ff df <80> 3c > > 08 00 74 08 48 89 ef e8 c8 63 62 00 4c 8b 5d 00 48 8b 3c 24 > > RSP: 0018:ffffc90002cb7be0 EFLAGS: 00010206 > > RAX: 0000000000000006 RBX: 0000000000000048 RCX: dffffc0000000000 > > RDX: 0000000000000000 RSI: 00000000000002a4 RDI: ffffffff8b517360 > > RBP: 0000000000000030 R08: ffffffff8191f8eb R09: 1ffff11004039e86 > > R10: dffffc0000000000 R11: ffffffffa00016d0 R12: 000000007fff0000 > > R13: ffff88801f84a800 R14: ffffc90002cb7df0 R15: 000000007fff0000 > > FS: 00007f897e849900(0000) GS:ffff888063a00000(0000) knlGS:0000000000000000 > > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > > CR2: 00007f897d771b08 CR3: 00000000195fe000 CR4: 0000000000350ef0 > > DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 > > DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 > > Call Trace: > > <TASK> > > __seccomp_filter+0x46f/0x1c70 kernel/seccomp.c:1222 > > syscall_trace_enter+0xa4/0x140 kernel/entry/common.c:52 > > syscall_enter_from_user_mode_work include/linux/entry-common.h:168 [inline] > > syscall_enter_from_user_mode include/linux/entry-common.h:198 [inline] > > do_syscall_64+0x5d/0x150 arch/x86/entry/common.c:79 > > entry_SYSCALL_64_after_hwframe+0x67/0x6f > > Has anything changed in BPF in this area lately? > > > RIP: 0033:0x7f897ed171e4 > > Code: 84 00 00 00 00 00 44 89 54 24 0c e8 36 58 f9 ff 44 8b 54 24 0c > > 44 89 e2 48 89 ee 41 89 c0 bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d > > 00 f0 ff ff 77 34 44 89 c7 89 44 24 0c e8 68 58 f9 ff 8b 44 > > RSP: 002b:00007ffd4ae74a60 EFLAGS: 00000293 ORIG_RAX: 0000000000000101 > > RAX: ffffffffffffffda RBX: 00005627cd785ed0 RCX: 00007f897ed171e4 > > RDX: 0000000000290000 RSI: 00007f897f010d0a RDI: 00000000ffffff9c > > RBP: 00007f897f010d0a R08: 0000000000000000 R09: 0034353132303865 > > R10: 0000000000000000 R11: 0000000000000293 R12: 0000000000290000 > > R13: 00007ffd4ae74d20 R14: 0000000000000000 R15: 00007ffd4ae74e28 > > </TASK> > > Modules linked in: > > ---[ end trace 0000000000000000 ]--- > > RIP: 0010:__bpf_prog_run include/linux/filter.h:691 [inline] > > RIP: 0010:bpf_prog_run include/linux/filter.h:698 [inline] > > RIP: 0010:bpf_prog_run_pin_on_cpu include/linux/filter.h:715 [inline] > > RIP: 0010:seccomp_run_filters+0x17a/0x3f0 kernel/seccomp.c:426 > > Code: 00 00 e8 99 36 d2 ff 0f 1f 44 00 00 e8 cf 58 ff ff 48 8d 5d 48 > > 48 83 c5 30 48 89 e8 48 c1 e8 03 48 b9 00 00 00 00 00 fc ff df <80> 3c > > 08 00 74 08 48 89 ef e8 c8 63 62 00 4c 8b 5d 00 48 8b 3c 24 > > RSP: 0018:ffffc90002cb7be0 EFLAGS: 00010206 > > RAX: 0000000000000006 RBX: 0000000000000048 RCX: dffffc0000000000 > > RDX: 0000000000000000 RSI: 00000000000002a4 RDI: ffffffff8b517360 > > RBP: 0000000000000030 R08: ffffffff8191f8eb R09: 1ffff11004039e86 > > R10: dffffc0000000000 R11: ffffffffa00016d0 R12: 000000007fff0000 > > R13: ffff88801f84a800 R14: ffffc90002cb7df0 R15: 000000007fff0000 > > FS: 00007f897e849900(0000) GS:ffff888063a00000(0000) knlGS:0000000000000000 > > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > > CR2: 00007f521f8ca000 CR3: 00000000195fe000 CR4: 0000000000350ef0 > > DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 > > DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 > > ---------------- > > Code disassembly (best guess): > > 0: 00 00 add %al,(%rax) > > 2: e8 99 36 d2 ff call 0xffd236a0 > > 7: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) > > c: e8 cf 58 ff ff call 0xffff58e0 > > 11: 48 8d 5d 48 lea 0x48(%rbp),%rbx This LEA looks like it's calculating the address of prog->insnsi. > > 15: 48 83 c5 30 add $0x30,%rbp > > 19: 48 89 e8 mov %rbp,%rax > > 1c: 48 c1 e8 03 shr $0x3,%rax > > 20: 48 b9 00 00 00 00 00 movabs $0xdffffc0000000000,%rcx > > 27: fc ff df > > * 2a: 80 3c 08 00 cmpb $0x0,(%rax,%rcx,1) <-- trapping instruction Here you can see - the access happens at rax+rcx, which is (rbp>>3)+0xdffffc0000000000. rbp is the actual pointer that will be accessed; this shift-right-and-add-0xdffffc0000000000 pattern is how inline KASAN instrumentation determines the shadow address. > > 2e: 74 08 je 0x38 Normally we jump over the next two instructions, if the KASAN shadow is 0 (which means fully accessible)... > > 30: 48 89 ef mov %rbp,%rdi > > 33: e8 c8 63 62 00 call 0x626400 ... continue here: > > 38: 4c 8b 5d 00 mov 0x0(%rbp),%r11 And here's the actual access to rbp, which is probably loading the prog->bpf_func. > > 3c: 48 8b 3c 24 mov (%rsp),%rdi > > What's the movabs? I don't have anything like that in my vmlinux binary > output. Is this KASAN perhaps? Yes, inline KASAN. > > Regardless, I don't see how prog could be NULL. :( It shouldn't be > possible without some kind of major refcounting bug.