DF-3813 / verdict.json
{ "finding_id": "DF-3813", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain use-after-free: mpr_wait_command()s lksleep wakeup path (mpr.c:3578,3604) and mpr_request_polled()s poll loop (mpr.c:3641) never re-check for a concurrent reallocating diag reset, so a sleeper woken by mprsas_complete_all_commands() (which does not free WAKEUP commands, mpr_sas.c:1178-1199) keeps using a cm that mpr_iocfacts_free() kfreed with the whole commands[] array (mpr.c:791-797). Only the own-timeout path checks MPR_FLAGS_REALLOCATED (mpr.c:3611). Runtime verdict inconclusive: the audit guest has the mpr driver compiled in (kldstat: 142 pci/mpr) but no SAS3 controller (/dev/mpr0 absent), so the race cannot be fired; the compiled+run PoC harness exits with the honest ENXIO negative (guest_run.log).", "exploit_chain": "concurrent waiter (root ioctl / config-page / SATA-ID / TM) + facts-changing diag reset -> mpr_iocfacts_free() kfrees commands[] under sleeper -> sleeper returns 0 with dangling cm -> caller callout_stop/cm_reply read/mpr_free_command() on freed slab -> mpr_free_command re-inserts alias of a live new-pool command into req_list -> one mpr_command owned twice -> kernel memory corruption (controlled via subsequent SCSI/ioctl command contents); full chain requires hardware, not demonstrated on this guest.", "evidence": "VERDICT.md (step-by-step source trace); guest_run.log (BUILD_OK + open /dev/mpr0: No such file or directory + kldstat 142 pci/mpr); fix.diff (generation-counter fix).", "kernel_refs": [ "sys/dev/raid/mpr/mpr.c:3578", "sys/dev/raid/mpr/mpr.c:3611", "sys/dev/raid/mpr/mpr.c:3641", "sys/dev/raid/mpr/mpr.c:791-797", "sys/dev/raid/mpr/mpr_sas.c:1178" ], "poc_changes": "Added missing #include <errno.h> to the seed sketch (first build failed, see build.log); otherwise the harness was structurally correct (ioctl 210 struct verified against mpr_ioctl.h:116-124, Function offset 3 per mpi2.h:855-866).", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 40, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o poc poc_concurrent_reinit.c -lpthread", "run_cmd": "./poc", "code_hash": "402eed4d687f8ab13073beff1758bfefb6199f9c98baaae609fe6e263a7021b1", "notes": "mpr.c-side sibling of DF-3800 (same realloc pass; DF-3800 covers stale sassc->maxtargets). Adjacent caller-side consequences for other files pass-2: mpr_sas_lsi.c:1173 callout_stop on NULL/dangling cm; mpr_user.c:846 mprsas_free_tm(NULL).", "recommended_fix": "Bump a realloc generation counter on every reallocating pass and have mpr_wait_command()/mpr_request_polled() treat a generation change as command freed (*cmp = NULL, ENXIO).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix validation impossible on this guest: no SAS3 controller, so neither baseline nor single-fix kernel can drive the mpr_wait_command sleeper path (fix.diff authored against the read-only sys/ tree, never applied).", "fix_evidence": "fix.diff (generation counter in mprvar.h + checks at both wait sites); guest_run.log device-absence proof." } |