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

32-bit integer overflow/underflow bypasses firmware truncation size check

Summary

intel_uc_fw.c CSS-header fields are attacker-controlled u32 dwords read directly from firmware blob at :79. (a) header_size=(header_size_dw-modulus_size_dw-key_size_dw-exponent_size_dw)*sizeof(u32) at 83-85 is u32 arithmetic no underflow guard; (b) ucode_size=(size_dw-header_size_dw)*sizeof(u32) at 96 another unguarded u32 subtraction *4 truncates; (c) decisive bug at :109 size=header_size+ucode_size+rsa_size all three u32 so sum overflows at 2^32 BEFORE zero-extending into size_t -> truncation guard "if(fw->datasize<size)" at :110 bypassed. Example: header_size=0x80 ucode_size=0xFFFFFF00 rsa_size=0x100 sums to 0x100000180 wraps to 0x80 in u32 passes guard for any blob>=128 bytes. Impact bounded: GEM object uses real fw->datasize not overflowed size; RSA read clamped by sg_pcopy_to_buffer; DMA and WOPCM consume same wrapped u32 internally consistent no GPU-vs-kernel memory corruption. Net: malformed firmware defeats truncation validation -> fails RSA verification -> graceful fallback to execlist mode (DoS of optional GuC/HuC offload). Attacker: anyone placing firmware blob in search path (root /boot module param mode 0400) or compromised firmware package. AV:L/PR:L/AC:L, A:L. Fix: promote sum to size_t; add underflow guards on u32 subtractions matching upstream Linux hardening.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2064 Β· 7 files
FileTypeDescriptionSize
VERDICT.md verdict full narrative, mechanism, citations, fix-build result 4.0 KB ↓ raw
README.md readme evidence-pack overview 1.6 KB ↓ raw
fix.diff suggested-fix standalone git-apply-able fix (authored post-verification) 1.6 KB view raw
build.sh repro-script combined-fix nativekernel build command 254 B view raw
run.sh repro-script runtime trigger (HW/module-gated) 421 B view raw
fix_build.log build-log full combined-fix kernel build output (rc=0, -Werror, 0 warnings) 5.6 MB ↓ download
env.txt environment guest uname / kern.version / cc version 289 B view raw
README.md readme evidence-pack overview
↓ download raw

DF-2064 β€” PoC evidence pack

Finding: 32-bit integer overflow bypasses firmware truncation size check (i915 GuC/HuC) Class: Integer overflow / underflow (CWE-190) | Impact ceiling: none | Confidence: certain

Status

SOURCE-CONFIRMED. The defect is verified real by line-by-line tracing of the audited sys/ tree, but it is HW/module-gated: the audit guest lacks the required hardware/module (Intel Skylake/Kabylake/Broxton iGPU + write access to the firmware blob path (root /boot module param at mode 0400)), so it cannot be triggered at runtime here. See VERDICT.md for the full mechanism and citations.

How to reproduce (on a guest that HAS the hardware)

  1. Ensure the gating precondition is met: Intel Skylake/Kabylake/Broxton iGPU + write access to the firmware blob path (root /boot module param at mode 0400).
  2. Apply fix.diff to confirm the cited path changes; before the fix the cited code path exhibits the behaviour described in VERDICT.md.
  3. The original finding markdown describes the runtime trigger.

Build validation (Phase 8)

fix.diff applies cleanly to pristine source. All nine findings' fixes were built together; see VERDICT.md "Phase 8" and fix_build.log:

make -j6 nativekernel KERNCONF=X86_64_GENERIC   # rc=0, -Werror, 0 warnings

Files

  • VERDICT.md β€” full narrative, mechanism, citations, fix-build result
  • fix.diff β€” standalone git apply-able fix (authored post-verification)
  • fix_build.log β€” full combined-fix kernel build output (rc=0, -Werror)
  • env.txt β€” guest environment for this verification
  • manifest.json β€” machine-readable catalog
VERDICT.md verdict full narrative, mechanism, citations, fix-build result
↓ download raw

DF-2064 β€” VERDICT

Verdict: REPRODUCED (source-confirmed; HW/module-gated β€” not runtime-triggerable on this guest) Class: Integer overflow / underflow (CWE-190) Impact ceiling: none Confidence: certain

Reproduction status

This finding is HW/module-gated and could not be triggered at runtime on the audit guest. The guest has no Atheros NIC, no AdvanSys HBA, no Intel i915 GPU, no AMD Southern-Islands GPU, no LSI MegaRAID controller, and no loaded dm device (only vtnet0). The PoC therefore cannot reach the vulnerable path at runtime here. The bug is instead confirmed by line-by-line source tracing against the audited sys/ tree (master DEV, the same commit the kernel was built from) β€” the defect is real and deterministic given the hardware/module, which is the standard bar for HW-gated driver findings.

Mechanism (source-confirmed)

uc_fw->header_size, ucode_size and rsa_size are u32 (intel_uc_fw.h:69,71,73). At intel_uc_fw.c:109 the sum size = header_size + ucode_size + rsa_size is computed in 32-bit (unsigned int) arithmetic and only THEN zero-extended into size_t, so it wraps at 2^32 BEFORE the truncation guard if (fw->datasize < size) (:110). Example: header_size=0x80, ucode_size=0xFFFFFF00, rsa_size=0x100 sum to 0x100000180 which wraps to 0x80 in u32, passing the guard for any blob >= 128 bytes. The u32 subtractions at :83-85 and :96 are likewise unguarded. Impact is bounded: the GEM object uses the real fw->datasize and RSA is clamped by sg_pcopy_to_buffer, so the malformed fw just fails RSA verification and falls back gracefully -- a truncation-validation bypass / hardening gap, not memory corruption.

Gating precondition: Intel Skylake/Kabylake/Broxton iGPU + write access to the firmware blob path (root /boot module param at mode 0400)

Exploit chain

Not applicable β€” this finding is HW/module-gated on the audit guest (no triggering hardware/module present), so no userspace-driven escalation chain can be exercised here. Per AGENT.md Phase 6 the valid hard blocker that applies is "vulnerable code path is reachable only on hardware/module absent from this guest." The realistic impact ceiling for the bug itself is none as documented above (panic for the corruption-class bugs on default GENERIC with INVARIANTS ON; dos for the I/O-wedge/stall bugs; none/graceful-fallback for the validation-bypass and pure missing-check / resource-leak findings).

Fix (authored, git-apply-able)

Promote the :109 sum to size_t via explicit casts before adding, and add underflow guards on the u32 subtractions at :83-85 (modulus+key+exponent <= header_size_dw) and :96 (size_dw >= header_size_dw), matching upstream Linux hardening.

The standalone diff is fix.diff in this directory. It applies cleanly to the pristine audited source (git apply --check verified).

Phase 8 β€” combined fix-build validation

All nine findings' fixes were applied together to a single in-guest source tree and built with make -j6 nativekernel KERNCONF=X86_64_GENERIC. The DragonFly kernel compile invokes cc ... -Werror on every translation unit, so this is a true -Werror build.

  • Result: === NK_DONE rc=0 === (full log: fix_build.log)
  • Compiler errors in changed files: 0
  • Compiler warnings (entire tree): 0
  • Patched kernel.stripped sha256: 6086a989213ef7be890180f60edc0fd9827bf60efbc3ed8c969724746e250df6
  • Baseline (unpatched) kernel: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Because the PoC is HW/module-gated, the fix could not be runtime A/B tested on this guest (no way to trigger the bad behaviour on either the baseline or the patched kernel). The fix is therefore classified fix_status = not_testable (compile-validated + source-traced to close the cited path), which is the honest result for HW-gated findings.

Kernel references (verified during this trace)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

COMPILE-VALIDATED, runtime not_testable. fix.diff applies cleanly and built into the combined nativekernel build: === NK_DONE rc=0 ===, -Werror, 0 warnings in intel_uc_fw.c. Source-traced closure: the sum is promoted to size_t and the subtractions are guarded, so a malformed CSS header is rejected at -ENOEXEC instead of wrapping past the truncation check. Runtime A/B impossible (no iGPU).

baseline intel_uc_fw.c:109 size = (u32)sum, wraps; patched: (size_t)sum + underflow guards. Combined build === NK_DONE rc=0 ===, 0 warnings. Gating: no iGPU => runtime not_testable.
↓ fix.diffcombined-fix kernel built rc=0 -Werror (kernel.stripped sha256 6086a989...); not booted β€” runtime not_testable (HW-gated: no Intel iGPU)

Confirmed kernel references

Detail

Exploit chain

none (integer-overflow validation bypass with graceful RSA-verification fallback; no memory corruption, no leak, no panic). No chain; impact ceiling is a defeated truncation check that results only in optional GuC/HuC offload being unavailable.

Evidence (decisive lines)

intel_uc_fw.h:69/71/73 u32 header_size/rsa_size/ucode_size; intel_uc_fw.c:109 size = header_size+ucode_size+rsa_size (u32 arithmetic); :110 if (fw->datasize < size) bypassed by wrap. Combined-fix build: === NK_DONE rc=0 === (-Werror).

PoC changes

findings/poc/DF-2064/ populated: VERDICT.md, fix.diff (promote :109 sum to size_t via explicit casts; add underflow guards on the u32 subtractions at :83-85 and :96), README.md, build.sh, run.sh, env.txt, fix_build.log, manifest.json.

Verified recommended fix

At intel_uc_fw.c:109 compute size = (size_t)header_size + (size_t)ucode_size + (size_t)rsa_size so the sum cannot wrap before the truncation check; add underflow guards at :83-85 (reject if modulus+key+exponent > header_size_dw) and :96 (reject if size_dw < header_size_dw). Matches upstream Linux hardening. Standalone diff in findings/poc/DF-2064/fix.diff; matches finding proposal.

Verdict

SOURCE-CONFIRMED (HW-gated). uc_fw->header_size, ucode_size and rsa_size are u32 (intel_uc_fw.h:69,71,73). At intel_uc_fw.c:109 the sum size = header_size + ucode_size + rsa_size is computed in 32-bit (unsigned int) arithmetic and only then zero-extended into size_t, so it wraps at 2^32 BEFORE the truncation guard if (fw->datasize < size) (:110). Example: header_size=0x80, ucode_size=0xFFFFFF00, rsa_size=0x100 sum to 0x100000180 which wraps to 0x80 in u32, passing the guard for any blob >= 128 bytes. The u32 subtractions at :83-85 and :96 are likewise unguarded. Impact is bounded: the GEM object uses the real fw->datasize and RSA is clamped by sg_pcopy_to_buffer, so the malformed fw just fails RSA verification and falls back gracefully β€” a truncation-validation bypass / hardening gap, not memory corruption.