bios_sigsearch(9) performs no parameter sanity checks: paralen==0 infinite-loops the kernel, negative paralen/sigofs walk outside the BIOS scan window (kernel-API robustness; all in-tree callers pass constants)
Summary
Only the start address is validated; the int parameters are trusted blindly: paralen==0 never advances (infinite loop), paralen<0 walks the pointer backwards while the loop condition stays true (unbounded OOB read down the kernel map), sigofs<0 reads before the window, siglen<=0 degenerates bcmp into a false match. Pure defense-in-depth: the sole in-tree caller (ipmi_smbios) passes compile-time constants and ipmi is module-only on GENERIC (live-verified: symbol present, function never executes at boot, window contains only SeaBIOS firmware bytes). Entry-guard fix proposed; introspection-only evidence (a root-triggerable kernel hang is not a vulnerability).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5666 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| introspect.sh | β | 2.7 KB | view raw | |
| introspection.log | β | 1.3 KB | view raw | |
| README.md | β | 3.6 KB | β raw | |
| VERDICT.md | β | 5.1 KB | β raw | |
| env.txt | β | 595 B | view raw | |
| fix.diff | β | 667 B | view raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 4.2 KB | view raw |
DF-5666 β bios_sigsearch(9) lacks parameter sanity checks (API robustness, Info)
File: sys/platform/pc64/x86_64/bios.c (94 LOC, pass 2 GLM 5.3)
Severity: Info (hardening; no reachable impact with in-tree callers)
Evidence class: guest-live introspection + code reading (per reservation DF-5666..DF-5678)
What this is
bios.c on pc64 provides exactly ONE live symbol: bios_sigsearch
(bios.c:66-94) β a read-only scan of the 128 KB BIOS ROM window
(physical 0xe0000-0xfffff, accessed via BIOS_PADDRTOVADDR(x) = x + KERNBASE,
pc64/include/pc/bios.h:36). The bios16/bios32 trampoline machinery is
explicitly absent (bios.c:30-34); guest nm /boot/kernel/kernel confirms only
bios_sigsearch exists (ffffffff80c19220 T bios_sigsearch).
The hardening gap
The function validates only its start argument (bios.c:72-78, accepting
[0xe0000, 0x100000] inclusive β start == 0x100000 degenerates to zero
iterations, harmless). It performs no validation of siglen, paralen,
sigofs:
paralen == 0βsp += paralen(bios.c:91) never advances β infinite kernel loop if no match exists.paralen < 0βspwalks backwards out of the window, below KERNBASE+0xe0000; the loop condition(sp + sigofs + siglen) < end(bios.c:84) stays true while the pointer decreases β unbounded OOB read below the window until an unmapped kernel VA faults (panic).sigofs < 0βbcmp(sp + sigofs, β¦)(bios.c:87) reads before the window.siglen <= 0β degenerate bcmp (len 0 "matches" instantly β first paragraph returned).
Why this is Info and not higher
- Sole in-tree caller is
ipmi_smbios.c:186:bios_sigsearch(SMBIOS_START, SMBIOS_SIG, SMBIOS_LEN, SMBIOS_STEP, SMBIOS_OFF)=(0xf0000, "_SM_", 4, 0x10, 0)β all compile-time constants (pc64/include/pc/bios.h:56-60). No user-controlled value reaches any argument, ever. - Guest-verified reachability:
ipmi(4)is NOT compiled into X86_64_GENERIC (0 matches in the config); it exists only as/boot/kernel/ipmi.koand is not loaded on the audit guest β the function does not execute at all in the default configuration. Loading it requires root. - The data scanned is firmware-written ROM (attacker model = hostile
firmware = ring -2, out of scope); consequences of hostile SMBIOS tables
in the downstream parser were already filed as DF-2107
(
sys/dev/misc/ipmi/ipmi_smbios.c, Medium) β not this file's bug.
Reproduce the introspection (not a crash PoC)
Phase V crash PoC is skipped per contract for Info hardening findings β
"triggering" the gap requires a root-loaded kernel module passing paralen=0,
which is not a threat model (root can halt the box anyway). The reserved
evidence class for this file is guest-live introspection:
# guest up (dfbsd-qemu/vm.sh status) dfbsd-qemu/vm.sh run_root "$(cat introspect.sh)"
Expected decisive output (see introspection.log):
ffffffff80c19220 T bios_sigsearch <- live symbol ipmi NOT compiled into X86_64_GENERIC ... <- never runs at boot 000159c0 5f 53 4d 5f 28 1f 02 08 ... <- "_SM_" anchor at phys 0xf59c0
Fix
One entry guard in bios_sigsearch (see fix.diff); not applied to sys/
(read-only audit tree), not kernel-rebuild-validated (fix_status:
not_testable β no triggerable behavior to A/B for an Info finding).
Artifacts
introspect.sh+introspection.logβ the full guest-live evidenceenv.txtβ guest environmentVERDICT.mdβ full narrativefix.diffβ git-apply-able hardening guardmanifest.json,verdict.json
DF-5666 β VERDICT
Status: untested (Phase V crash PoC skipped per contract for Info
hardening findings; guest-live introspection performed instead β it is the
evidence class reserved for this file)
Reproduced: no (nothing user-reachable to reproduce)
Impact: none
Confidence: certain (about the facts: the gap exists; the reachability
analysis is code- and guest-verified)
The four checklist questions, answered with evidence
(a) What does this file actually provide on pc64?
Exactly one function: bios_sigsearch (sys/platform/pc64/x86_64/bios.c:66-94).
The file header states it outright: "We cannot make bios16 nor bios32 calls β¦
However, searching for bios rom signatures can be useful" (bios.c:30-34).
Guest proof (introspection.log):
ffffffff80c19220 T bios_sigsearch (bios16/bios32: none)
The header (sys/platform/pc64/include/pc/bios.h:34-37) declares only
bios_sigsearch plus the BIOS_PADDRTOVADDR/BIOS_VADDRTOPADDR KERNBASE-offset
macros and SMBIOS constants/structs. There is no pc32 twin in this tree
(sys/platform/ contains only pc64 and vkernel64).
(b) Who reaches it, and when?
One in-tree caller: sys/dev/misc/ipmi/ipmi_smbios.c:186, inside
ipmi_smbios_probe(), reached from ipmi_smbios_identify()
(ipmi_smbios.c:222-250, results cached via the ipmi_probed state machine)
called by the ipmi(4) identify/probe routines: ipmi_isa.c:58, ipmi_pci.c:98,
ipmi_smbus.c:60,81. Every argument is a compile-time constant from
machine/pc/bios.h:56-60 (0xf0000, "_SM_", 4, 0x10, 0).
Guest proof: ipmi has 0 matches in /usr/src/sys/config/X86_64_GENERIC,
kldstat -v shows it not loaded, /boot/kernel/ipmi.ko exists as a module
only, and there is no /dev/ipmi*. So on a stock system the function never
executes; when it does (root kldload ipmi or a custom kernel), it runs once
per boot at driver-identify time with firmware data as its only input.
(c) Buffer math
- Loop bound
(sp + sigofs + siglen) < end(bios.c:84) is strict β the last byte compared is atend-2, i.e. the scan stays in bounds and is in fact one byte more conservative than necessary (a signature ending exactly at 0x100000 would be missed; for SMBIOS this is physically moot since the 31-byte EPS cannot fit there). No OOB read on any in-tree path. startis validated to [0xe0000, 0x100000] (bios.c:72-78);start == 0maps to BIOS_START; anything else returns 0.start == 0x100000degenerates to zero iterations. Pointer range KERNBASE+0xe0000 .. KERNBASE+0x100000 β mapped (guest/dev/memdump of the window succeeded and returned firmware bytes), return conversionBIOS_VADDRTOPADDRfits u_int32_t.- The gap:
siglen/paralen/sigofsare trusted ints.paralen==0β infinite loop (bios.c:91);paralen<0β backwards OOB walk below the window (bios.c:84 condition stays true as sp decreases);sigofs<0β reads before the window (bios.c:87);siglen<=0β degenerate bcmp match. No in-tree caller can pass such values β kernel-API hardening only.
(d) Boot-time attacker model
The scanned window is firmware-written (BIOS ROM / reserved area). Guest dump
shows SeaBIOS's SMBIOS 2.8 EPS: _SM_ at physical 0xf59c0 (16-byte
aligned), _DMI_ intermediate anchor, structure table at 0xf59e0, 10
structures β i.e., had ipmi.ko been loaded, bios_sigsearch would have
matched at 0xf59c0 and handed that address to the downstream parser. A
hostile firmware controlling that data is ring -2 compromise (out of model),
and the downstream table-walk risk was already filed as DF-2107 against
sys/dev/misc/ipmi/ipmi_smbios.c (Medium) in this audit β deliberately not
re-filed here (different file, pass-2 discipline).
Why not a crash PoC
The only way to exercise the unvalidated-parameter paths is a kernel module
calling bios_sigsearch(0xf0000, "_SM_", 4, 0, 0) β loading it requires
root, and a root-triggerable kernel hang is not a vulnerability. Per contract,
Info hardening findings skip Phase V; the guest-live introspection above is
the verification for this file's reservation.
Fix validation
fix.diff adds an entry guard (sig==NULL / siglen<=0 / paralen<=0 / sigofs<0
β return 0). Not kernel-rebuild-tested: fix_status not_testable β there is
no triggerable misbehavior on a stock guest to A/B against (the guard is
pure defense-in-depth for future callers).
Bottom line
bios.c is a 94-line, correct-on-every-in-tree-path, boot-time,
firmware-data-only signature scan. The sole reportable item is the missing
API parameter validation β filed as Info hardening. Everything else
considered (bounds math, entry validation, mapping, TOCTOU/locking β none
applicable, no locks, boot-time identify only, caller caches results) is
negative; see the JSON result block's negative_notes.
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 adds the parameter guard at bios_sigsearch entry. Not kernel-rebuild-validated: there is no triggerable misbehavior on a stock guest (no in-tree caller can pass bad parameters; ipmi(4) is module-only), so no baseline-vs-patched A/B exists for an Info hardening change.
findings/poc/DF-5666/fix.diff (git-apply-able unified diff against sys/platform/pc64/x86_64/bios.c; never applied to the read-only audit tree).
Confirmed kernel references
- sys/platform/pc64/x86_64/bios.c:66
- sys/platform/pc64/x86_64/bios.c:72
- sys/platform/pc64/x86_64/bios.c:84
- sys/platform/pc64/x86_64/bios.c:87
- sys/platform/pc64/x86_64/bios.c:91
- sys/platform/pc64/include/pc/bios.h:34
- sys/platform/pc64/include/pc/bios.h:36
- sys/platform/pc64/include/pc/bios.h:56
- sys/dev/misc/ipmi/ipmi_smbios.c:186
Detail
Evidence (decisive lines)
findings/poc/DF-5666/introspection.log β decisive lines: 'ffffffff80c19220 T bios_sigsearch' (live symbol, no bios16/32), 'ipmi NOT compiled into X86_64_GENERIC => bios_sigsearch does not run at boot' (reachability), hexdump line '000159c0 5f 53 4d 5f 28 1f 02 08' = _SM_ SMBIOS 2.8 EPS at phys 0xf59c0 (firmware-only input, window mapped). Code: bios.c:72-78 start validation vs. absent siglen/paralen/sigofs validation; bios.c:84/87/91 the trusting arithmetic. VERDICT.md has the full narrative.
PoC changes
No crash PoC authored (Info hardening; Phase V PoC skipped per contract). Verification artifact is introspect.sh β exact guest-live introspection commands (symbol table, config/module reachability, /dev/mem window dump) β executed verbatim via vm.sh run_root; log in introspection.log.
Verified recommended fix
Add an entry guard to bios_sigsearch: reject sig==NULL, siglen<=0, paralen<=0, sigofs<0 (return 0) before computing sp β see fix.diff.
Verdict
bios.c on pc64 provides exactly one live symbol, bios_sigsearch (bios.c:66-94), a boot-time read-only scan of the firmware-controlled 128KB BIOS window (phys 0xe0000-0xfffff via KERNBASE offset). The bounds math is correct on every in-tree path: the strict loop bound (sp+sigofs+siglen) < end (bios.c:84) keeps every compared byte inside the window, start is validated to [0xe0000,0x100000] (bios.c:72-78), and the sole in-tree caller (ipmi_smbios.c:186) passes only compile-time constants (0xf0000, "SM", 4, 0x10, 0 from machine/pc/bios.h:56-60). Guest-live introspection confirms: symbol live at ffffffff80c19220; ipmi(4) not compiled into X86_64_GENERIC and not loaded (module-only, /boot/kernel/ipmi.ko), so the function does not even execute in the default configuration; the /dev/mem dump of the window shows SeaBIOS's SMBIOS 2.8 SM anchor at physical 0xf59c0, i.e. firmware data is the only input. The reportable gap is API robustness only: siglen/paralen/sigofs are trusted ints, so a future kernel caller passing paralen==0 gets an infinite kernel loop (bios.c:91) and paralen<0 or sigofs<0 walks outside the window (bios.c:84/87). No user-reachable trigger exists; crash PoC skipped per contract for Info hardening findings, with guest introspection (the reserved evidence class) captured in introspection.log.
No comments yet.