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.

Reply via email to