[Bug 284682] use-after-free in SCSI g_resize_provider_event()
Date: Sun, 09 Feb 2025 13:57:46 UTC
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: bugs@FreeBSD.org
Reporter: rtm@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.