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.
Recommended fix
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)
PoC verification
Evidence pack
findings/poc/DF-2639 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_testableConfirmed 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).
No comments yet.