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

acpi_thinkpad attach: kfreeenv(NULL) on the nosmbios path β†’ panic("trying to free NULL pointer") when exactly one SMBIOS env var is unset

Summary

attach jumps to nosmbios when EITHER smbios env var returns NULL, then unconditionally kfreeenvs both pointers. kfreeenv has no NULL guard and DragonFly's _kfree panics on NULL. Inherited FreeBSD code that was harmless there (kfree(NULL) is a no-op on FreeBSD) but panics on DragonFly - a DF-specific divergence. Boot-time panic on ThinkPad firmware with a partial SMBIOS Type-1 table. NULL-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4287 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
fix.diff β€” 368 B view raw
manifest.json β€” 785 B view raw
verdict.json β€” 2.0 KB view raw

DF-4287 β€” acpi_thinkpad attach: kfreeenv(NULL) panics when exactly one SMBIOS env var is absent

What

acpi_thinkpad_attach() [sys/dev/acpica/acpi_thinkpad/acpi_thinkpad.c:607-624]:

maker = kgetenv("smbios.system.maker");
product = kgetenv("smbios.system.product");
if (maker == NULL || product == NULL)
        goto nosmbios;
...
nosmbios:
    kfreeenv(maker);          /* acpi_thinkpad.c:623 */
    kfreeenv(product);        /* acpi_thinkpad.c:624 */

When exactly one of the two kenv variables is unset, the other pointer is NULL and is passed to kfreeenv(), which forwards directly to kfree() [sys/kern/kern_environment.c:332-336 — no NULL guard]. DragonFly's _kfree() panics on NULL: sys/kern/kern_slaballoc.c:1404-1406 — if (ptr == NULL) panic("trying to free NULL pointer"). (Note kenv_isdynamic is 1 on any loader-booted system, so the kfreeenv→kfree path is live.)

Result: boot-time / driver-attach kernel panic on a ThinkPad whose firmware provides the IBM0068/LEN0068/LEN0268 ACPI device but a missing or malformed SMBIOS system-maker/product string (smbios(4) sets these only when an SMBIOS table with the corresponding structure exists).

Trigger

Hardware-gated boot path (ThinkPad + broken/partial SMBIOS table); no user interaction. Not reproducible on the audit guest (not a ThinkPad).

Fix

fix.diff β€” NULL-guard both kfreeenv calls. (A kernel-wide alternative β€” making kfreeenv NULL-tolerant like FreeBSD's kfree(NULL) no-op β€” is noted in the verdict but the minimal driver-local fix suffices.)

VERDICT.md
↓ download raw

VERDICT β€” DF-4287

Status: inconclusive (source-certain; attach path cannot run on this guest) Impact: boot-time / attach-time kernel panic (panic("trying to free NULL pointer")) Confidence: certain

Proof chain

  1. acpi_thinkpad.c:607-610 β€” kgetenv() returns NULL for an unset variable; if either is NULL the code jumps to nosmbios without distinguishing which one.
  2. acpi_thinkpad.c:622-624 β€” nosmbios: kfreeenv(maker); kfreeenv(product); β€” when exactly one is NULL, that kfreeenv call receives NULL.
  3. sys/kern/kern_environment.c:332-336: c void kfreeenv(char *env) { if (kenv_isdynamic) kfree(env, M_KENV); } no NULL check; kenv_isdynamic is set on loader (dynamic kenv) boots.
  4. sys/kern/kern_slaballoc.c:1404-1406: c if (ptr == NULL) panic("trying to free NULL pointer"); β†’ kernel panic.

Reachability

  • Driver attaches only on ThinkPad ACPI HID IBM0068/LEN0068/LEN0268 [acpi_thinkpad.c:375, 448-456].
  • Needs smbios.system.maker XOR smbios.system.product missing β€” both are populated by smbios(4) from the SMBIOS Type 1 structure; a table with a truncated/absent Type 1, or a string slot emptied, yields NULL from kgetenv. Corner-case firmware, hence Low.
  • This inherited FreeBSD code (acpi_ibm.c) was safe on FreeBSD because FreeBSD's free(NULL)/kfree(NULL) is a no-op; DragonFly's allocator panics β€” a DF-specific latent divergence.

Why not runtime-verified

The audit guest is not ThinkPad hardware (no IBM0068/LEN0068/LEN0268 ACPI node), so acpi_thinkpad_attach never executes there. The panic sequence (kgetenv NULL β†’ kfreeenv β†’ _kfree KKASSERT-free explicit panic) is source-certain. Module compiles cleanly on the guest (verified during this run).

Fix

fix.diff β€” NULL-guard the two kfreeenv calls (minimal, matches the surrounding style). Kernel-wide hardening (kfreeenv tolerating NULL) would also be reasonable but is out of scope for a single-driver fix.

Bottom line

Deterministic NULL-kfree panic chain on a corner-case ThinkPad firmware configuration; boot-time only; Low severity but zero-cost to fix.

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

Not testable: attach path requires IBM0068/LEN0068/LEN0268 ACPI hardware. The panic chain is explicit in source (_kfree panics on NULL by design); the fix is a two-line NULL guard.

source-level: fix.diff vs acpi_thinkpad.c:622-624
↓ fix.diffper-fix-DF-4287

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md proof chain: acpi_thinkpad.c:607-624 -> kern_environment.c:332-336 -> kern_slaballoc.c:1404-1406

PoC changes

no seed; not runtime-testable (driver never attaches on this guest)

Verified recommended fix

NULL-guard the two kfreeenv() calls at the nosmbios label (fix.diff)

Verdict

acpi_thinkpad_attach() jumps to nosmbios when EITHER smbios.system.maker or product is NULL but unconditionally calls kfreeenv() on both; kfreeenv() forwards NULL straight to kfree() and DragonFly's _kfree() panics on NULL (kern_slaballoc.c:1405). Inherited FreeBSD code that was harmless there (FreeBSD kfree(NULL) is a no-op) but panics on DragonFly. Source-certain chain; requires ThinkPad ACPI hardware with corner-case SMBIOS env, absent from this guest.