mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #41] kernel: ftruncate of a mapped file leaves the pages past EOF mapped (no SIGBUS, stale data) #36
Labels
No labels
bug
documentation
enhancement
enhancement
good first issue
help wanted
help wanted
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
github-starred/RhapsodiOS#36
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @raynorpat on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/41
Symptom
After
ftruncate()shrinks a file that a process has mappedMAP_SHARED, the process can still read the old contents of the pages past the new end of file, and writes there succeed. No SIGBUS is raised. POSIX requires SIGBUS for a reference to a mapped page that lies wholly beyond EOF.Reproduce
stress-mmap truncfrom the new stress suite (src/Commands/stress, ffs-perf branch). It maps a 16-page file shared, fills it, truncates it to 5 pages, then touches page 7:A standalone check gave the same result:
ftruncatefrom 16 pages to 5, reading page 5 or page 7 through the existing shared map returns the old data (0x11fill).It fails on both kernels tested:
7ed3069ab).So it is not the MapFS read/write coherency bug.
Where to look
ffs_truncate()insrc/kernel-7/bsd/ufs/ffs/ffs_inode.c:v_vm_info->mapped), the code at lines 307-314 and 350-356 skipsvnode_pager_setsize(). It relies onmapfs_trunc()(lines 265-280) instead.mapfs_trunc()deals with the kernel's own MapFS mapping. Nothing appears to shrink the pager object or remove the pages a usermmaphas resident beyond the new size. So those pmap entries survive and keep pointing at the old pages, and a later fault in that range is satisfied instead of failing.A fix would likely remove all mappings of pages at or above the new size (pmap/vm_object page removal over the truncated range) and shrink the pager size, even when the vnode is mapped. Faults past EOF would then go to the pager and fail with SIGBUS.
Impact
This is a correctness bug for anything that truncates a file it, or another process, has mapped: databases, logs,
dyldcaches.Related: the MapFS coherency fix
7ed3069ab(write()vsmmap).@raynorpat commented on GitHub (Oct 14, 2026):
Fixed on branch
fs-races(commit76c273754), not yet merged to master.Root cause:
vno_flush()freed the object's pages withoutpmap_remove_all(), so user mappings kept pointing at the old pages.mapfs_trunc()also skipped the flush entirely when the kernel had no MapFS mapping of the file, andvnode_pagein()zero-filled pages past EOF instead of failing them.Fix:
vno_flushremoves pmap mappings before freeing and finds the object through the pager when the kernel has no mapping;mapfs_truncflushes in the unmapped case;vnode_pageinreturns PAGER_ERROR for pages wholly past EOF.Tested on an i386 QEMU guest (snapshot of the v2 image):
stress-mmap -d /tmp truncisFAIL reason=no-sigbuson the stock kernel every time, and PASS on the fixed kernel (5 of 5). The rest of the stress suite (proc, vm, fs, net, ipc, thread) shows no regressions. Two failures are the same on the stock kernel, so they are not caused by this change:mmap mixed(data mismatch) andthread signal(timeout).Not checked: ext2fs, msdosfs, hfs and nfs truncate paths. They call
mapfs_trunctoo, so they pick up thevno_flushchange, but I did not test them.