wal_add() leaks the previous W/A list kmalloc on every growth step; i915_wa_lists are never freed at teardown
Summary
wal_add overwrites wal->list after memcpy without kfree of the old buffer, and nothing in i915 ever frees gt_wa_list/engine->wa_list. Dormant today (largest in-tree list is 9 entries; growth path never runs), but any future platform table >16 entries leaks the prior buffer per growth step and hot-unplug leaks all lists per cycle. kfree-after-memcpy fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4417 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | β | 2.2 KB | β raw | |
| verdict.json | β | 1.8 KB | view raw | |
| fix.diff | β | 273 B | view raw | |
| manifest.json | β | 522 B | view raw | |
| README.md | β | 695 B | β raw |
DF-4417 β i915: wal_add() leaks prior W/A list buffer on growth; no teardown free
- File: sys/dev/drm/i915/intel_workarounds.c:604-618
- Severity: Info Β· CWE: CWE-401 Β· Confidence: certain (defect), dormant today
- Status: untested β growth path (>16 W/A entries in one list) is not taken by any in-tree platform (max = 9, ICL GT list).
Build / Run / Expected
Static defect. To exercise: extend any *_gt_workarounds_init() past 16 entries
and observe the kmalloc'd buffer from the previous growth step being leaked at
wal->list = list;. fix.diff adds the missing kfree. See VERDICT.md.
Artifacts
VERDICT.md Β· verdict.json Β· fix.diff Β· manifest.json
DF-4417 β i915 wal_add() leaks the previous W/A list buffer on every growth; lists are never torn down
Verdict (short)
Status: untested β dormant leak; no in-tree platform list exceeds 16
entries, so the growth path (IS_ALIGNED(wal->count, grow) with
wal->list != NULL) never executes on current hardware, and the QEMU guest
has no Intel GPU. Filed as Info-severity hardening/correctness.
Root cause (path:line)
sys/dev/drm/i915/intel_workarounds.c:604-618 (wal_add):
if (IS_ALIGNED(wal->count, grow)) { /* Either uninitialized or full. */
list = kmalloc_array(ALIGN(wal->count + 1, grow), sizeof(*wa), GFP_KERNEL);
...
if (wal->list)
memcpy(list, wal->list, sizeof(*wa) * wal->count);
wal->list = list; /* <-- old kmalloc'd buffer leaked */
}
When wal->count hits 16/32/β¦, a new buffer is allocated and the old pointer
is overwritten without kfree(). Additionally there is no teardown anywhere:
rg "kfree.*(wa_list)" over sys/dev/drm/i915/*.c finds nothing, so
gt_wa_list and per-engine wa_list allocations (one per engine) live until
the drm device is destroyed β and since DRM is compiled into the kernel here,
a GPU hot-unplug/re-attach cycle leaks each list once per cycle.
Reachability today
Largest lists in this file (counted): ICL GT list = 9 entries, ICL RCS engine list = 8, Gen9 RCS = 6 β all < 16, so only the initial allocation (no leak) runs. The leak becomes live the day a platform table grows past 16 entries.
Fix
findings/poc/DF-4417/fix.diff β free the old buffer after the copy.
(A full fix would also add an intel_wa_list_free() on driver teardown;
minimal growth-leak fix included here.)
Reproduce
Not reproducible in this environment (needs gen8+ Intel GPU, plus a W/A table
16 entries to take the growth path). Static proof only: the overwritten pointer is unreferenced after
wal->list = list;β no other alias exists.
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 validation not performed: no triggering hardware (Intel gen10/11 GPU / 82541 NIC) exists in the audit QEMU guest; fix.diff authored from line-accurate source analysis only.
Fix validation not performed: no triggering hardware (Intel gen10/11 GPU / 82541 NIC) exists in the audit QEMU guest; fix.diff authored from line-accurate source analysis only.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md; intel_workarounds.c:604-618; grep shows no kfree of any i915_wa_list in sys/dev/drm/i915/
PoC changes
No runnable PoC authored (hardware-dependent path); source-trace verification only.
Verified recommended fix
kfree(wal->list) after the memcpy in wal_add (see fix.diff); ideally add intel_wa_list_free() at device teardown.
Verdict
wal_add (intel_workarounds.c:604-618) overwrites wal->list after memcpy without kfree of the old buffer, so every 16-entry growth step leaks the previous kmalloc; additionally no code path ever frees gt_wa_list/engine->wa_list. Dormant today: largest in-tree list is 9 entries (ICL GT), so the growth path never runs; leak-per-device-cycle only if lists grow past 16 or DRM device is destroyed/re-created. Info severity hardening.
No comments yet.