DF-2209 / build.log
[build] tree file /usr/src/sys/dev/drm/drm_sysfs.c sha256 = 331166b1130dfe2235da2353efc1cdd64eda7aa6bab57f5cf4916fa94f2172eb
[build] verbatim drm_sysfs_connector_add (lines 31-46) sha256 = 15deb3e46a5fd983fa53499a5a855dca07502a399f55a3da1c7fba05fc26e13b
[build] verbatim drm_sysfs_connector_remove (lines 48-54) sha256 = 8916a8ba87e65359b4489b5d059a4a9e6d1640325f0de54d2f01a1ec24db934a
[build] extraction cross-check OK (line-range == pattern)
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|# DF-2208 + DF-2209 combined fix (same file, same two functions).
|#
|# DF-2208: drm_sysfs_connector_add() dereferenced kzalloc() and stored
|# kasprintf() results without NULL checks and reported success
|# unconditionally. With the checks in place a failed allocation returns
|# -ENOMEM so drm_connector_register() (drm_connector.c:444-446) fails
|# loudly instead of publishing a half-built connector device.
|#
|# DF-2209: drm_sysfs_connector_remove() left connector->kdev dangling
|# after kfree(), so the `if (connector->kdev) return 0;` guard in
|# drm_sysfs_connector_add() (drm_sysfs.c:35-36) turns any
|# re-registration into a silent no-op returning success while every
|# consumer (intel_dp.c:5251,5253, intel_sdvo.c:2198) operates on freed
|# memory. NULLing the pointer closes the stale-pointer window; freeing
|# the kasprintf'd name fixes the per-cycle string leak seen in the rig.
|#
|# NOTE (audit): on DragonFly GFP_KERNEL is M_WAITOK|M_CACHEALIGN
|# (sys/dev/drm/include/linux/gfp.h:16) and _kmalloc() without M_NULLOK
|# panics or blocks rather than returning NULL (sys/kern/kern_slaballoc.c:
|# 869-878, 1722-1724, 1786-1800), so the DF-2208 NULL returns model the
|# Linux OOM window; the checks are still correct hardening for the file
|# as written and match the kvasprintf() NULL discipline the DFly drm
|# shim itself already implements (drm_dragonfly.c:78-79).
|--- a/sys/dev/drm/drm_sysfs.c
|+++ b/sys/dev/drm/drm_sysfs.c
--------------------------
Patching file sys/dev/drm/drm_sysfs.c using Plan A...
Hunk #1 succeeded at 37.
Hunk #2 succeeded at 56.
done
[build] fixed variants extracted (fix.diff + rename only)
[build] assembled df22_rig.c: head + BASELINE-verbatim x2 + FIXED-verbatim x2 + tail
In file included from df22_rig.c:26:
/usr/src/sys/sys/systm.h:216:5: warning: conflicting types for built-in function 'log' [-Wbuiltin-declaration-mismatch]
int log (int, const char *, ...) __printflike(2, 3);
^~~
[build] built /root/df22/DF-2209/df22_rig.ko (baseline + fixed in ONE module)
--- run.log (matrix) ---
DF22 RIG start: kernel-resident verbatim drm_sysfs.c add/remove (baseline) + fix.diff variants
DF22 shims: kzalloc/kfree/kasprintf mirror slab.h:35,38 and drm_dragonfly.c:65-94 with fault injection ONLY at the cited call sites
DF2208 CASE A (control): verbatim add/remove
DF22 [drm_sysfs] adding "DP-1" to sysfs
DF2208 ret=0 kdev=0xfffff80116c07a80 name='card0-DP-1' (content capture: card%d-%s of dev->primary->index=0, connector->name='DP-1')
DF2208 offsetof(struct device, kobj.name)=8 sizeof(struct device)=56 (M_CACHEALIGN -> power-of-2 class)
DF22 [drm_sysfs] removing "DP-1" from sysfs
DF2208 after remove: connector->kdev still reads 0xfffff80116c07a80 (baseline remove NULLs nothing); alloc_events=2 free_events=1 (delta alloc=2 free=1)
DF2208 SIDE-OBSERVATION: the kasprintf'd kobj.name string is never freed by remove() (drm_sysfs.c:52-53 frees kdev only) -> leaks 1 allocation per connector add/remove cycle
DF2208 CASE B (kasprintf==NULL injected at drm_sysfs.c:40):
DF22 [drm_sysfs] adding "DP-2" to sysfs
DF2208 ret=0 kdev=0xfffff80116c07a80 kobj.name=0
DF2208 VERDICT: SILENT NULL PLANTED -- function reports SUCCESS (ret==0) with kdev->kobj.name==NULL; first consumer deref (intel_dp.c:5251 connector->kdev->kobj.name) hits NULL
DF22 [drm_sysfs] removing "DP-2" from sysfs
DF2209 CASE D (UAF cycle): verbatim add -> remove -> reclaim -> consume -> re-add -> remove
DF22 [drm_sysfs] adding "DP-3" to sysfs
DF2209 [1] add: kdev=0xfffff80116c07a80 (kobj.name='card0-DP-3')
DF22 [drm_sysfs] removing "DP-3" from sysfs
DF2209 [2] remove: kfree(0xfffff80116c07a80) done; connector->kdev still == 0xfffff80116c07a80 (DANGLING -- drm_sysfs.c:52-53 never NULLs it)
DF2209 [3] reclaim: fresh kmalloc(56, M_DF22, M_WAITOK|M_CACHEALIGN|M_ZERO) returned 0xfffff80116c07a80 == freed A (IMMEDIATE SAME-ZONE REUSE); block poisoned 0x41 and kobj.name forged
DF2209 [4] consumer read connector->kdev->kobj.name -> 'DF2209-PWNED-BY-RECLAIM' (UAF READ returns ATTACKER-CONTROLLED data; was 'card0-DP-3' before the free)
DF2209 [5] re-add: ret==0 (SUCCESS) with rig_kzalloc_calls UNCHANGED (3) -- guard at drm_sysfs.c:35-36 turned add into a silent no-op on the DANGLING pointer
DF22 [drm_sysfs] removing "DP-3" from sysfs
DF2209 [6] second remove: production kfree()d the RECLAIMED live object 0xfffff80116c07a80 owned by another allocator (CROSS-OBJECT FREE); owner still holds the pointer
DF2209 [6] owner-after-free read R->kobj.name -> 0xfffff8008f90d100 (== forged string, intact)
DF2209 VERDICT: UAF CONFIRMED through verbatim drm_sysfs.c code (dangling ptr + silent re-add + controlled reclaim + cross-object free)
DF2208 CASE E (fix.diff variants, same injections):
DF2208 [kzalloc-NULL] ret=-12 (-ENOMEM) kdev=0 -- NO fault, caller (drm_connector.c:445-446) can fail loudly
DF2208 [kasprintf-NULL] ret=-12 kdev=0 kobj.name=0 -- kdev freed and NULLed, NO silent NULL planted
DF2209 CASE F (fix.diff variants):
DF22 [drm_sysfs] adding "DP-5" to sysfs
DF22 [drm_sysfs] removing "DP-5" from sysfs
DF2209 [1] fixed remove: kdev=0 (NULLed: fix breaks the dangling pointer)
DF22 [drm_sysfs] adding "DP-5" to sysfs
DF2209 [2] fixed re-add: FRESH allocation 0xfffff80116c07b40 != freed 0xfffff80116c07a80 (and != interposed 0xfffff80116c07a80), kzalloc_calls 6 -> 7, name='card0-DP-5' -- no dangling, no silent no-op
DF22 [drm_sysfs] removing "DP-5" from sysfs
DF2209 VERDICT: fixed cycle clean
DF22 RIG done (baseline kzalloc-NULL panic case ARMED: sysctl kern.df2208_fire=1)