if_ic: SIOCSIFMTU frees the working buffers before checking the replacement allocations β on kmalloc failure installs NULL ic_obuf/ic_ifbuf while still updating if_mtu
Summary
MTU change kmallocs replacements, kfrees the working pair, installs unchecked (M_WAITOK CAN return NULL for 64KB-class under pressure on DFly), and updates if_mtu - a working interface becomes a DF-2001 NULL-deref panic machine deterministically. Source-proven; PoC declined (forcing kmem exhaustion risks the shared guest). Allocate-first+rollback fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4941 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| fix.diff | β | 1.4 KB | view raw | |
| verdict.json | β | 2.9 KB | view raw | |
| lpbbfake.c | β | 3.4 KB | view raw | |
| Makefile.harness | β | 102 B | β download | |
| build.sh | β | 76 B | view raw |
DF-4941 β if_ic: SIOCSIFMTU frees the working buffers even when the replacement allocation fails (installs NULL ic_obuf/ic_ifbuf)
File: sys/dev/netif/ic/if_ic.c (icioctl SIOCSIFMTU, lines 205β223)
Verdict: source-proven, PoC not executed (triggering the allocation
failure deterministically requires kernel-memory exhaustion; see VERDICT.md).
Fix (fix.diff) authored from the line-accurate root cause, compile-checked
against the guest tree, not runtime-validated.
The bug
sc->ic_ifbuf = kmalloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_WAITOK); /* 211 */
sc->ic_obuf = kmalloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_WAITOK); /* 214 */
if (iptr) kfree(iptr, M_DEVBUF); /* 217 */
if (optr) kfree(optr, M_DEVBUF); /* 220 */
sc->ic_if.if_mtu = ifr->ifr_mtu; /* 222 */
There is no NULL check and no rollback. M_WAITOK is widely assumed to
never fail, but on DragonFly that is false for these sizes:
- the generic SIOCSIFMTU wrapper allows
72 <= ifr_mtu <= 65535(sys/net/if.c:2272, IF_MINMTU/IF_MAXMTU, sys/net/if_var.h:558-559), so the allocation is up to 65539 bytes; - allocations at/above
ZoneLimitgo tokmem_slab_alloc()which returns NULL on failure even with M_WAITOK (sys/kern/kern_slaballoc.c:942-950); - for zone-sized allocations, zone creation failure also unwinds to NULL
(kern_slaballoc.c:1070-1072
if (z == NULL) goto fail;).
Under kernel-map pressure, ifconfig ic0 mtu 65535 therefore:
1. kfree()s the working buffer pair (lines 217-220),
2. installs two NULL pointers,
3. still updates if_mtu (line 222) and returns success.
From that moment the interface is armed with the DF-2001 sinks:
icintr INTR_RECEIVE *sc->ic_cp++ (line 309) and icoutput's
bcopy(..., sc->ic_obuf, ICHDRLEN) (line 358) dereference NULL β a
deterministic kernel panic (local DoS) on the next traffic or received
byte, caused by an ordinary error path rather than the DF-2001 pre-alloc
condition.
Fix
Allocate the replacements first with M_NULLOK; on failure free only the
failed new allocations and return ENOBUFS, leaving the working pair and
the old MTU untouched (fix.diff).
Why not runtime-verified
The failure precondition is kmem exhaustion for a 64 KB-class allocation. Deliberately exhausting kernel memory on the shared audit guest risks wedging unrelated subsystems (and the VM), and the mechanism (source trace above) is deterministic. Recorded as untested rather than risking a low-value DoS of the test environment; the root cause is fully cited.
DF-4941 VERDICT β source-proven error-path lifecycle defect; PoC not executed (untested)
Root cause (path:line)
sys/dev/netif/ic/if_ic.c icioctl SIOCSIFMTU (205-223): both replacement
buffers are kmalloc'd, the OLD working pair is kfree'd, and if_mtu is
updated β with no NULL check on either kmalloc result and no rollback.
M_WAITOK does not guarantee success on DragonFly for the sizes reachable
here:
- if.c:2272 admits
72 <= ifr_mtu <= 65535(IF_MINMTU/IF_MAXMTU, if_var.h:558-559) β allocation size up to 65539 bytes; - size >= ZoneLimit β
kmem_slab_alloc(size, PAGE_SIZE, flags); on failure the allocator returns NULL even with M_WAITOK (sys/kern/kern_slaballoc.c:942-950); - below ZoneLimit, zone creation failure unwinds to NULL as well (kern_slaballoc.c:1070-1072).
Under kernel-map pressure ifconfig ic0 mtu 65535 therefore kfree()s the
working pair, installs ic_ifbuf == NULL and ic_obuf == NULL, sets
if_mtu = 65535 and reports success. The interface is then armed with the
(pass-1, DF-2001) NULL-deref sinks:
- TX: icoutput if_ic.c:358
bcopy((char *)&hdr, sc->ic_obuf, ICHDRLEN)β NULL write, panic, on the next routed packet; - RX: icintr INTR_START caches
sc->ic_cp = sc->ic_ifbuf(NULL) and INTR_RECEIVE executes*sc->ic_cp++ = *ptr(if_ic.c:267,309) β NULL write, panic, on the next byte from the i2c master.
Note the RX check sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN (305) does
NOT protect against this: with the NULL buffers there is nothing to
overflow into β the write happens at address 0+xfercnt.
Classification and honest status
- status: untested β the failure precondition is kernel-memory exhaustion for a 64 KB-class allocation; forcing it on the shared audit guest risks wedging unrelated subsystems. No PoC was executed.
- The defect itself is deterministic and fully source-traced; the sinks' behavior (NULL write panic) is established by DF-2001's analysis (same sink lines). Confidence: certain for the code defect, likely for end-to-end impact.
- Severity: Low β privileged trigger (SIOCSIFMTU is SYSCAP_RESTRICTEDROOT-gated, if.c:2265), requires kmem pressure, impact ceiling = local kernel panic (DoS).
Non-overlap
- DF-2000: SIOCSIFMTU race (serializer vs output/intr on the freed old buffers) β different mechanism (no race here; this is the sequential error path).
- DF-2001: the NULL-deref sinks pre-allocation β same sink lines, but this finding is the distinct deterministic path that installs NULLs on a previously-working interface while claiming success.
Fix
fix.diff (authored after source verification, compile-checked style,
not runtime-validated β fix_status not_tested): allocate replacements
with M_WAITOK|M_NULLOK first; on any failure free only the new
allocations and return ENOBUFS, leaving the working pair and old MTU
intact.
Kernel references
- sys/dev/netif/ic/if_ic.c:205-223 (no NULL check / no rollback)
- sys/dev/netif/ic/if_ic.c:358, 267+309 (the NULL-deref sinks, DF-2001)
- sys/net/if.c:2261-2292 (generic SIOCSIFMTU, bounds, privilege)
- sys/net/if_var.h:558-559 (IF_MINMTU 72 / IF_MAXMTU 65535)
- sys/kern/kern_slaballoc.c:942-950 (large alloc returns NULL with M_WAITOK)
- sys/kern/kern_slaballoc.c:1070-1072 (zone create failure β NULL)
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff authored from line-accurate root cause (allocate-first, ENOBUFS rollback, M_NULLOK); not runtime-validated because the baseline failure path itself could not be triggered without risking the shared guest.
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md root-cause trace: if_ic.c:211-222 (unchecked kmallocs, free of working pair, mtu update); kern_slaballoc.c:942-950 and 1070-1072 (M_WAITOK NULL paths); if.c:2272 (bounds admitting 65535).
PoC changes
No PoC executed (deliberate): deterministic trigger requires kernel-memory exhaustion; documented as untested with full source trace instead.
Verified recommended fix
Allocate replacement buffers with M_WAITOK|M_NULLOK before freeing the working pair; on failure free only the new allocations and return ENOBUFS without changing if_mtu (fix.diff).
Verdict
Source-proven error-path defect, not executed: SIOCSIFMTU (if_ic.c:205-223) kfree()s the working ic_obuf/ic_ifbuf pair and installs the new kmalloc results without any NULL check or rollback, and still updates if_mtu. DF kmalloc CAN return NULL here despite M_WAITOK (sizes up to 65539 B admitted by if.c:2272; >=ZoneLimit path returns NULL on kmem_slab_alloc failure, kern_slaballoc.c:942-950; zone-create failure likewise, :1070-1072), so under kernel-memory pressure ifconfig ic0 mtu 65535 deterministically leaves the interface with NULL buffers β arming the DF-2001 NULL-deref sinks (icoutput:358 bcopy, icintr:309 write) for a panic on the next packet/byte. PoC not executed because the trigger requires kmem exhaustion (risk of wedging the shared guest); root cause fully cited path:line. Fix authored (allocate-first with M_NULLOK, ENOBUFS rollback) but not runtime-validated.
No comments yet.