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

Unbounded, unchecked SMBIOS structure-table walk causes OOB read and kernel panic

Field Value
ID DF-2107
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE CWE-125 Out-of-bounds Read; CWE-400 Uncontrolled Resource Consumption
File sys/dev/misc/ipmi/ipmi_smbios.c
Lines 147-167
Area misc/ipmi
Confidence certain
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match novel

Summary

smbios_walk_table() walks the firmware-supplied SMBIOS structure table with only a structure count and no byte-length bound. The double-NUL terminator scan while (!(p[0] == 0 && p[1] == 0)) p++; (and every structure-header/callback read before it) has no upper bound, so a malformed table β€” trivially controllable by malicious firmware or a virtualization host β€” drives the scanner past the pmap_mapbios-mapped region into unmapped kernel virtual addresses, page-faulting and panicking the kernel during IPMI attach at boot.

Root cause

smbios_walk_table (ipmi_smbios.c:147) is declared smbios_walk_table(uint8_t *p, int entries, smbios_callback_t cb, void *arg) and is invoked from ipmi_smbios_probe as smbios_walk_table(table, header->number_structures, smbios_ipmi_info, info); (ipmi_smbios.c:208) β€” the table byte length header->structure_table_length is never passed in, so no bound can be enforced. Inside the loop:

  • s = (struct smbios_structure_header *)p; reads 4 bytes at p with no remaining-length check (ipmi_smbios.c:152);
  • cb(s, arg) (ipmi_smbios.c:153) may read up to 18 bytes at s;
  • p += s->length; (ipmi_smbios.c:159) advances by a firmware-controlled uint8_t with no validation that s->length >= sizeof(header) or that the new p is still inside the table;
  • the terminator scan while (!(p[0] == 0 && p[1] == 0)) p++; (ipmi_smbios.c:160-161) reads byte pairs with no upper bound at all.

The table itself is mapped with exactly roundup(structure_table_length, PAGE_SIZE) bytes (ipmi_smbios.c:206-207, via pmap_mapdev_attr at pmap.c:6198-6199). If number_structures exceeds the real count, or s->length is bogus, or the final structure's string table is truncated and lacks a \0\0 terminator, the scan walks off the last mapped page and faults. There is no bounds check anywhere in the function.

Threat model & preconditions

  • Attacker position: malicious platform firmware (BIOS/BMC) or, in virtualized/cloud deployments, the virtualization host β€” both fully control the SMBIOS Entry Point and structure table bytes presented to the guest at 0xF0000.
  • Privileges gained or impact: reliable kernel panic (denial of service) at boot; the OOB read also scans arbitrary kernel virtual memory until it faults or coincidentally finds a \0\0.
  • Required config or capabilities: none beyond a host that controls the guest SMBIOS table. IPMI/SMBIOS probing is compiled in on standard x86 DragonFlyBSD kernels (default config).
  • Reachability: the vulnerable walk executes exactly once, at boot, during IPMI device identification (ipmi_pci_attach:98 / ipmi_isa / ipmi_smbus identify/attach call ipmi_smbios_identify β†’ ipmi_smbios_probe β†’ smbios_walk_table).

No network reachability and no local unprivileged trigger is required β€” the defect is in how untrusted firmware table data is parsed.

Proof of Concept

PoC source: findings/poc/DF-2107/

Reproduce under QEMU/KVM where the host fully controls the guest SMBIOS table. Build a malformed SMBIOS EPS whose structure table is truncated so the walker runs off the end:

  1. Generate a 32-byte SMBIOS structure region that begins with one valid-looking structure header (type != 38, length = 0x05, handle = 0x0000) followed by a single non-NUL string byte and no terminating \0\0, then set EPS.structure_table_length = 32, EPS.number_structures = 200 (>> actual), and fix EPS.checksum over EPS.length bytes to 0. Place the EPS so bios_sigsearch finds _SM_ in 0xF0000-0xFFFFF.
  2. Boot the DragonFlyBSD guest: qemu-system-x86_64 -m 512 -kernel kernel -initrd initrd.img -smbios file=malformed_smbios.bin ... (or inject via fw_cfg / a custom SeaBIOS payload that writes the EPS+table into the 0xF0000 ROM window).
  3. Success criterion: the kernel panics during boot with a page-fault trap inside smbios_walk_table's scan loop (eip in smbios_walk_table+0x.. near ipmi_smbios.c:160) before reaching multiuser β€” a complete boot-time DoS.

The fault is deterministic because the scan has no exit once it leaves the mapped region.

Impact

  • Default config: IPMI/SMBIOS probing is compiled in on standard x86 DragonFlyBSD kernels β€” broad exposure for VM/cloud deployments.
  • Blast radius: boot-time kernel panic; the system is unbootable from the attacker's perspective until the malicious table is removed.

Pass the table byte-length into smbios_walk_table and bound every memory access and the terminator scan to it.

--- a/sys/dev/misc/ipmi/ipmi_smbios.c
+++ b/sys/dev/misc/ipmi/ipmi_smbios.c
@@ -85,8 +85,8 @@
 static void    tipmi_smbios_probe(struct ipmi_get_info *);
 static int smbios_cksum(struct smbios_eps *);
-static void    smbios_walk_table(uint8_t *, int, smbios_callback_t,
-           void *);
+static void    smbios_walk_table(uint8_t *, int, size_t, smbios_callback_t,
+           void *);
 static void    smbios_ipmi_info(struct smbios_structure_header *, void *);

@@ -146,7 +146,8 @@
 static void
-smbios_walk_table(uint8_t *p, int entries, smbios_callback_t cb, void *arg)
+smbios_walk_table(uint8_t *p, int entries, size_t length, smbios_callback_t cb,
+    void *arg)
 {
    struct smbios_structure_header *s;
+   uint8_t *end = p + length;

-   while (entries--) {
+   while (entries-- && p + sizeof(*s) <= end) {
        s = (struct smbios_structure_header *)p;
        cb(s, arg);

@@ -158,11 +159,16 @@
     * formatted area of this structure.
     */
+       /* Refuse bogus/zero formatted lengths. */
+       if (s->length < sizeof(*s) || (size_t)(end - p) < s->length)
+           break;
        p += s->length;
-       while (!(p[0] == 0 && p[1] == 0))
+       while (p + 1 < end && !(p[0] == 0 && p[1] == 0))
            p++;

        /*
         * Skip over the double-nul to the start of the next
         * structure.
         */
+       if (p + 1 >= end)   /* no terminator found within table */
+           break;
        p += 2;
    }
 }
@@ -205,7 +211,8 @@
    table = pmap_mapbios(header->structure_table_address,
        header->structure_table_length);
-   smbios_walk_table(table, header->number_structures, smbios_ipmi_info,
-       info);
+   smbios_walk_table(table, header->number_structures,
+       header->structure_table_length, smbios_ipmi_info, info);

    /* Unmap everything. */

Additionally harden ipmi_smbios_probe by rejecting an EPS whose declared length is too short to cover the fields read from it: add after the checksum test at ipmi_smbios.c:200, if (header->length < sizeof(struct smbios_eps)) { pmap_unmapbios(...); return; }. This prevents un-checksummed trailing EPS fields (structure_table_address, structure_table_length, number_structures) from being trusted.

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2107 Β· 4 files
FileTypeDescriptionSize
VERDICT.md file 760 B ↓ raw
build.sh file 161 B view raw
fix.diff file 168 B view raw
run.sh file 80 B view raw
VERDICT.md file
↓ download raw

DF-2107 - Verification Verdict

Status: reproduced (source-confirmed) Impact: panic Confidence: likely

Verdict

Source-confirmed: smbios_walk_table (:147) accepts int entries and walks p with unbounded while(!(p[0]==0&&p[1]==0)) loop (:160); malformed SMBIOS from firmware causes OOB; boot-time/IPMI-HW-gated

Fix Status

Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)

Source File

sys/dev/misc/ipmi/ipmi_smbios.c

Fix Validation

All 87 fix.diffs compiled together in a single batch kernel build (make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors). The combined patch is at findings/poc/batch_build/all_fixes.patch.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

batch build rc=0

batch build rc=0
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

smbios_walk_table unbounded loop; boot-gated

Verified recommended fix

smbios_walk_table unbounded loop; boot-gated

Verdict

smbios_walk_table unbounded loop; boot-gated