journal_setextattr journals the post-VOP, fully consumed uio: the extended-attribute VALUE is never recorded in the REDO stream
| Field | Value |
|---|---|
| ID | DF-2764 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N |
| CWE | CWE-1051 Incomplete Data Processing |
| File | sys/kern/vfs_jops.c |
| Lines | 1027 (op), 1039 (post-op uio journaling) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
journal_setextattr runs the op first and then journals JLEAF_FILEDATA from the SAME uio. VFS uio consumers advance iovecs in place, so after a successful op every iov_len==0 and jrecord_leaf_uio skips them all: the record carries only ATTRNAME and a post-op SEEKPOS β the attribute value never reaches the journal. Replicas replay empty extattrs; reversible undo cannot restore old values. journal_write demonstrates the required pattern (uio copied before the op). Latent today (no in-tree FS implements vop_setextattr) β activates the moment one does.
Recommended fix
Snapshot the uio before vop_journal_operate_ap (mirroring journal_write :859-868) β fix.diff in the pack (authored; not kernel-validated because no FS reaches the branch).
Timeline
- 2026-08-30 Discovered during pass-2 audit of vfs_jops.c (GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2764 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.1 KB | β raw | |
| VERDICT.md | β | 1.7 KB | β raw | |
| fs_check.log | β | 376 B | view raw | |
| fix.diff | β | 1.3 KB | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-2764 β journal_setextattr journals the post-VOP (fully consumed) uio: the extattr VALUE is never recorded
What
journal_setextattr() (sys/kern/vfs_jops.c:1014-1045) runs the
underlying op first (:1027 vop_journal_operate_ap) and then journals
the REDO payload with the SAME uio:
error = vop_journal_operate_ap(&ap->a_head); /* consumes a_uio */
if (error == 0) {
...
save = jrecord_push(jrec, JTYPE_REDO);
jrecord_write_uio(jrec, JLEAF_FILEDATA, ap->a_uio); /* already consumed! */
jrecord_pop(jrec, save);
}
VFS uio consumers advance the iovecs in place (uiomove() /
kern_subr.c:151 iov->iov_len -= cnt, iov_base += cnt) so after a
successful VOP_SETEXTATTR every iov_len is 0. jrecord_write_uio()
-> jrecord_leaf_uio() (sys/kern/vfs_journal.c:962-973) skips
zero-length iovecs, so the JLEAF_FILEDATA leaf carrying the attribute
value is never emitted; only JLEAF_ATTRNAME and a JLEAF_SEEKPOS with
the end offset are recorded. A replica replaying the stream sets an
empty/missing extended attribute; reversable-journal undo cannot
restore the old value. Contrast journal_write() (:859-868) which
explicitly copies the uio BEFORE the op ("UIO's don't retain
sufficient information to be reused once they've gone through the VOP
chain").
Reachability: any user able to set an extattr on a file in a root-journaled mount (the journal must be installed first; the trigger itself is unprivileged). NOTE: no in-tree DragonFly filesystem currently implements vop_setextattr (only smbfs, commented out, sys/vfs/smbfs/smbfs_vnops.c:121) and the guest has no extattr(1) userland β the defect is latent until an FS implements the VOP.
PoC status: not testable on this guest
setextattr/extattruserland absent (fs_check.log).- No in-tree FS returns success from VOP_SETEXTATTR, so the
error == 0branch that emits the broken record cannot be reached on the stock guest; a failing setextattr aborts the jrecord (jreclist_done(error)) and journals nothing.
Fix
Copy the uio before the op, exactly like journal_write() does (see fix.diff; not kernel-validated β feature absent on the guest).
DF-2764 β VERDICT
Status: not reproduced / not testable on this guest β static proof of mechanism (certain), feature absent at runtime.
Why not testable
The broken record is only emitted when the underlying VOP_SETEXTATTR
returns 0 (if (error == 0) in journal_setextattr, vfs_jops.c:1028).
- No in-tree DragonFly filesystem implements vop_setextattr (only
smbfs, commented out: sys/vfs/smbfs/smbfs_vnops.c:121); every FS
returns EOPNOTSUPP through the default vector, so the shim always
aborts its jrecord (jreclist_done(error)) and journals nothing.
- The guest also lacks extattr userland (setextattr not found;
fs_check.log).
Static proof (certain)
- journal_setextattr journals
ap->a_uioAFTER the op (vfs_jops.c:1039jrecord_write_uio(jrec, JLEAF_FILEDATA, ap->a_uio)). - VFS uio consumers mutate the iovec array in place
(sys/kern/kern_subr.c:151
iov->iov_len -= cnt, :150-ishiov_base += cnt; uiomove). After a successful op all iov_len == 0. - jrecord_leaf_uio skips zero-length iovecs
(sys/kern/vfs_journal.c:964-965
if (iov->iov_len == 0) continue;). => The JLEAF_FILEDATA leaf carrying the attribute value is never emitted; only JLEAF_ATTRNAME and a post-op SEEKPOS are recorded. journal_write() demonstrates the required pattern (uio copied BEFORE the op, vfs_jops.c:859-868, with the exact comment "UIO's don't retain sufficient information to be reused once they've gone through the VOP chain").
The defect is latent: it activates the moment any filesystem implements vop_setextattr (e.g. smbfs uncommenting it).
Fix
fix.diff (uio snapshot before the op, mirroring journal_write); authored but not kernel-validated β the code path cannot return success on the stock guest.
Fix verification
not_testablefix authored from the line-accurate mechanism; cannot be runtime-validated on the stock guest (no FS support)
['fix.diff']
Confirmed kernel references
Detail
Evidence (decisive lines)
['fs_check.log (setextattr absent; no FS support)', 'VERDICT.md static proof chain with path:line']
PoC changes
n/a - feature absent
Verified recommended fix
copy the uio before vop_journal_operate_ap in journal_setextattr, exactly like journal_write (fix.diff)
Verdict
journal_setextattr journals ap->a_uio after the underlying VOP consumed it (vfs_jops.c:1039); uiomove advances iov/iov_len in place (kern_subr.c:151) and jrecord_leaf_uio skips zero-length iovecs (vfs_journal.c:964), so the extattr VALUE leaf is never recorded - replication/undo of extattr sets is silently broken. Not testable on the stock guest: no in-tree FS implements vop_setextattr (smbfs's is commented out) and extattr userland is absent, so the error==0 branch that emits the broken record cannot be reached; mechanism proven statically with certainty.
No comments yet.