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

hammer2_inode_create_pfs never sets *errorp on its done2 error paths β€” snapshot ioctl silently succeeds without creating anything

Field Value
ID DF-2639
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N
CWE CWE-252 Unchecked Return Value
File sys/vfs/hammer2/hammer2_inode.c
Lines 1042-1051, 1108-1111
Area vfs
Confidence certain
Discovered 2026-08-29
Pass 2 (GLM 5.3 second pass)
Bucket hammer2
Reported pending
Known CVE none
CVE match novel

Summary

hammer2_inode_create_pfs sets *errorp = 0 at entry (inode.c:1006) but its two goto done2 error paths β€” scanlhc xop failed with an error other than ENOENT (:1042-1044) and the lhc directory-hash collision window exhausted (HAMMER2_ERROR_ENOSPC, :1048-1051) β€” return nip == NULL with *errorp still 0; the local error is discarded at done2. The sole caller, hammer2_ioctl_pfs_snapshot (hammer2_ioctl.c:878ff), then proceeds with error == 0 and reports success without a snapshot.

Threat model & preconditions

Root-only silent failure of HAMMER2IOC_PFS_SNAPSHOT: the operation reports success and nothing was created (availability/robustness).

Proof of concept

Trigger sketch (guest-run skipped per contract β€” Low): force the ENOSPC lhc path with ~32K hash-colliding PFS names or induce a backend I/O error, then HAMMER2IOC_PFS_SNAPSHOT returns 0 with no snapshot on media.

Assign *errorp = error before both goto done2 jumps in hammer2_inode_create_pfs.

Timeline

  • 2026-08-29 Discovered during pass-2 audit of hammer2_inode.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2639 Β· 3 files
FileTypeDescriptionSize
README.md β€” 1.2 KB ↓ raw
verdict.json β€” 1.1 KB view raw
manifest.json β€” 449 B view raw

DF-2639 β€” hammer2_inode_create_pfs never sets *errorp on its done2 error paths (silent snapshot/PFS-create failure)

What

sys/vfs/hammer2/hammer2_inode.c:988-1112 (hammer2_inode_create_pfs) sets *errorp = 0; at entry (line 1006) but the two goto done2 error paths leave it at 0 while returning nip == NULL:

  • scanlhc xop failed with an error other than ENOENT (I/O error) β€” hammer2_inode.c:1042-1044 (if (error) { if (error != ENOENT) goto done2; ... })
  • lhc exhausted the directory-hash collision window β€” hammer2_inode.c:1048-1051 (HAMMER2_ERROR_ENOSPC -> goto done2)

The local error is discarded at done2: (only hammer2_inode_unlock(pip) runs). The only caller, hammer2_ioctl_pfs_snapshot (hammer2_ioctl.c:878ff), then proceeds on nip == NULL with error == 0 and reports SUCCESS to the privileged caller without creating anything.

Impact: silent failure of HAMMER2IOC_PFS_SNAPSHOT (and any future caller); root-only, availability/robustness only. Low.

Fix

    if ((lhcbase ^ lhc) & ~HAMMER2_DIRHASH_LOMASK) {
        error = HAMMER2_ERROR_ENOSPC;
+       *errorp = error;
        goto done2;
    }
 ...
        if (error) {
            if (error != HAMMER2_ERROR_ENOENT)
                goto done2;

(set *errorp = error; before the first goto done2 as well).

Fix verification

not_testable
per-fix-DF-2639

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md quotes both goto-done2 paths and the caller's nip==NULL/error==0 handling

Verified recommended fix

Assign *errorp on both done2 error paths in hammer2_inode_create_pfs.

Verdict

hammer2_inode_create_pfs clears *errorp at entry and returns NULL through its done2 paths (scanlhc I/O error, lhc-window ENOSPC) without ever writing the real error, so hammer2_ioctl_pfs_snapshot reports success without creating the PFS/snapshot. Code-inspection finding; root-only silent failure; guest verification intentionally skipped per audit contract (Low).