https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=284682
Bug ID: 284682 Summary: use-after-free in SCSI g_resize_provider_event() Product: Base System Version: CURRENT Hardware: Any OS: Any Status: New Severity: Affects Some People Priority: --- Component: kern Assignee: b...@freebsd.org Reporter: r...@lcs.mit.edu Attachment #257348 text/plain mime type: Created attachment 257348 --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=257348&action=edit fake iscsi provider that cause a use-after-free in g_resize_provider_event() If a misbehaving SCSI device (e.g. a USB thumb drive) supplies certain invalid responses to READ CAPACITY (16) requests, dadone_proberc() can cause the corresponding g_provider to be free()d; but subsequently a pending g_resize_provider_event() can run with its g_hh00->pp set to that deallocated g_provider. A more detailed sequence of events: 1. the device at first returns some plausible READ CAPACITY (16) replies, then a reply with a huge sector size. 2. dadone_proberc sees block_size > maxphys, so it calls cam_periph_invalidate() 3. which results in g_orphan_provider putting pp on the g_doorstep list. 4. which results in one_event calling g_orphan_register(pp) 5. pp->consumers is empty, so g_orphan_register(pp) calls g_destroy_provider(pp) 6. meanwhile g_events contains a g_resize_provider_event, whose hh->pp is the provider that was just destroyed. Perhaps g_orphan_register()'s call to g_cancel_event(pp) should have removed that g_resize_provider_event -- but it does not, because the g_resize_provider_event's ref[] array is all NULLs (does not mention pp). pp isn't in the ref[] array becuase g_resize_provider() doesn't mention it in this call: g_post_event(g_resize_provider_event, hh, M_WAITOK, NULL); Although when I add pp to the g_post_event(), it results in recursive g_cancel_event() calls and a panic due to a recursive mutex acquire. I've attached a demo that acts as an iSCSI target. It stumbles over a KASSERT in an INVARIANTS kernel. Without INVARIANTS, the use-after-free occurs. # uname -a FreeBSD 15.0-CURRENT FreeBSD 15.0-CURRENT #406 main-n250997-12e772aee518-dirty: Sun Feb 9 07:28:47 EST 2025 rtm@xxx:/usr/obj/usr/rtm/symbsd/src/riscv.riscv64/sys/RTM riscv # cc iscsi34a.c # ./a.out Here's the backtrace for dadone_proberc()'s cam_periph_invalidate() call: #0 g_orphan_provider (pp=0xffffffd00d36ef00, error=6) at /usr/rtm/symbsd/src/sys/geom/geom_event.c:165 #1 0xffffffc00038e248 in g_wither_provider (pp=0xffffffd00d36ef00, error=6) at /usr/rtm/symbsd/src/sys/geom/geom_subr.c:456 #2 0xffffffc000387f32 in disk_gone (dp=0xffffffd001751400) at /usr/rtm/symbsd/src/sys/geom/geom_disk.c:995 #3 0xffffffc0000648aa in daoninvalidate (periph=0xffffffd00d5b1500) at /usr/rtm/symbsd/src/sys/cam/scsi/scsi_da.c:2086 #4 0xffffffc000009d24 in cam_periph_invalidate (periph=0xffffffd00d5b1500) at /usr/rtm/symbsd/src/sys/cam/cam_periph.c:691 #5 0xffffffc000068d58 in dadone_proberc (periph=0xffffffd00d5b1500, done_ccb=0xffffffd00d4c6220) at /usr/rtm/symbsd/src/sys/cam/scsi/scsi_da.c:4874 #6 0xffffffc00001259a in xpt_done_process (ccb_h=0xffffffd00d4c6220) at /usr/rtm/symbsd/src/sys/cam/cam_xpt.c:5374 #7 0xffffffc000014032 in xpt_done_td (arg=0xffffffc000babe80 <cam_doneqs>) at /usr/rtm/symbsd/src/sys/cam/cam_xpt.c:5429 #8 0xffffffc0003f63f6 in fork_exit (callout=0xffffffc000013f5c <xpt_done_td>, arg=0xffffffc000babe80 <cam_doneqs>, frame=0xffffffc08262bc50) at /usr/rtm/symbsd/src/sys/kern/kern_fork.c:1152 #9 0xffffffc0007ec68e in fork_trampoline () at /usr/rtm/symbsd/src/sys/riscv/riscv/swtch.S:370 And for the doorstop call to g_destroy_provider: #0 g_destroy_provider (pp=0xffffffd00d36ef00) at /usr/rtm/symbsd/src/sys/geom/geom_subr.c:803 #1 0xffffffc00038a2c2 in g_orphan_register (pp=0xffffffd00d36ef00) at /usr/rtm/symbsd/src/sys/geom/geom_event.c:221 #2 one_event () at /usr/rtm/symbsd/src/sys/geom/geom_event.c:246 #3 g_run_events () at /usr/rtm/symbsd/src/sys/geom/geom_event.c:281 #4 0xffffffc00038c73e in g_event_procbody (arg=<optimized out>) at /usr/rtm/symbsd/src/sys/geom/geom_kern.c:119 #5 0xffffffc0003f63f6 in fork_exit ( callout=0xffffffc00038c6e2 <g_event_procbody>, arg=0x0, frame=0xffffffc08264ec50) at /usr/rtm/symbsd/src/sys/kern/kern_fork.c:1152 #6 0xffffffc0007ec68e in fork_trampoline () at /usr/rtm/symbsd/src/sys/riscv/riscv/swtch.S:370 And just before the crash: #0 g_resize_provider_event (arg=0xffffffd00d399400, flag=<optimized out>) at /usr/rtm/symbsd/src/sys/geom/geom_subr.c:690 #1 0xffffffc00038a1d6 in one_event () at /usr/rtm/symbsd/src/sys/geom/geom_event.c:258 #2 g_run_events () at /usr/rtm/symbsd/src/sys/geom/geom_event.c:281 #3 0xffffffc00038c73e in g_event_procbody (arg=<optimized out>) at /usr/rtm/symbsd/src/sys/geom/geom_kern.c:119 #4 0xffffffc0003f63f6 in fork_exit ( callout=0xffffffc00038c6e2 <g_event_procbody>, arg=0x0, frame=0xffffffc08264ec50) at /usr/rtm/symbsd/src/sys/kern/kern_fork.c:1152 #5 0xffffffc0007ec68e in fork_trampoline () at /usr/rtm/symbsd/src/sys/riscv/riscv/swtch.S:370 (gdb) print hh->pp $1 = (struct g_provider *) 0xffffffd00d36ef00 (gdb) print/x *hh->pp $3 = {name = 0xdeadc0dedeadc0de, provider = {le_next = 0xdeadc0dedeadc0de, le_prev = 0xdeadc0dedeadc0de}, geom = 0xdeadc0dedeadc0de, consumers = { lh_first = 0xdeadc0dedeadc0de}, acr = 0xdeadc0de, acw = 0xdeadc0de, ace = 0xdeadc0de, error = 0xdeadc0de, orphan = { tqe_next = 0xdeadc0dedeadc0de, tqe_prev = 0xdeadc0dedeadc0de}, mediasize = 0xdeadc0dedeadc0de, sectorsize = 0xdeadc0de, stripesize = 0xdeadc0dedeadc0de, stripeoffset = 0xdeadc0dedeadc0de, stat = 0xdeadc0dedeadc0de, spare1 = 0xdeadc0de, spare2 = 0xdeadc0de, flags = 0xdeadc0de, aliases = {lh_first = 0xdeadc0dedeadc0de}, private = 0xdeadc0dedeadc0de, index = 0xdeadc0de} -- You are receiving this mail because: You are the assignee for the bug.