β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4417

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4417 Β· 5 files
FileTypeDescriptionSize
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

VERDICT.md
↓ download raw

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_testable
baseline no→ patch + rebuild →patched clean

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.

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.
↓ fix.diffper-fix-DF-4417

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.