โฌข DragonFlyBSD Kernel Audit
โ† dashboard
DF-0029

Unchecked copyin() in jrecord_data leaves stale kernel data in the journal stream

Field Value
ID DF-0029
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
CWE CWE-252 Unchecked Return Value
File sys/kern/vfs_journal.c
Lines 1093, 1140
Area kern
Confidence certain
Discovered 2026-06-29
Reported pending

Summary

Both copyin() call sites in jrecord_data() discard the return value. If the user buffer is invalidated between the size check and the copy (e.g. another thread munmap()s the range, or the page cannot be paged in), the FIFO destination retains whatever bytes it previously held (typically prior journal records) and the journal stream has no in-band marker to detect the substitution โ€” silent corruption of the authoritative recovery stream.

Root cause

sys/kern/vfs_journal.c:1093 (in-loop fill, while iterating stream_residual chunks) and :1140 (tail fill) both call copyin(buf, jrec->stream_ptr, ...) without inspecting the return value. On a non-zero return the destination is left unwritten (stale).

Threat model & preconditions

  • Attacker position: unprivileged user with write permission on a file on a journaled mount (journal installed by root).
  • Privileges gained or impact: no kernel memory-safety impact. Silent corruption of the journal stream: a recovery consumer replays substituted (stale) bytes as if they were the user's data, undermining trust in recovered filesystem state. Independent of DF-0028 โ€” any normal-sized writev() can trigger it via a racing munmap.
  • Reachability: thread A holds/munmaps the user buffer while thread B writev()s on the journaled FS; intermittently the journal FILEDATA leaf contains previous-transaction bytes.

Proof of concept (sketch)

thread A: mmap a page, writev() it on the journaled FS, and munmap() it in a
          tight loop from another thread racing the journal's copyin.
thread B: repeatedly writev(fd, &iov{mmap'd page}, 1).
Observe (offline, from the journal consumer) that some FILEDATA leaves carry
bytes that were not in the user buffer (previous-transaction residue).

Impact

Low โ€” journal integrity only, no kernel memory-safety impact. A maintainer- actionable robustness/correctness fix.

Check the copyin() return; on failure zero-fill the destination and log (the zero-fill makes the substitution detectable in the stream):

--- a/sys/kern/vfs_journal.c
+++ b/sys/kern/vfs_journal.c
@@ -1093 +1093,4 @@
-       copyin(buf, jrec->stream_ptr, jrec->stream_residual);
+       if (copyin(buf, jrec->stream_ptr, jrec->stream_residual) != 0)
+       bzero(jrec->stream_ptr, jrec->stream_residual);
@@ -1140 +1143,4 @@
-       copyin(buf, jrec->stream_ptr, bytes);
+       if (copyin(buf, jrec->stream_ptr, bytes) != 0)
+       bzero(jrec->stream_ptr, bytes);

References

Timeline

  • 2026-06-29 Discovered during automated file-by-file audit of sys/kern/vfs_journal.c.
  • pending Reported to DragonFlyBSD security contact.