mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #52] ext2fs: shrinking a mmap'd sparse file panics with "ext2fs_truncate1" #44
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#44
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 15, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/52
Symptom
On an ext2 scratch volume,
stress-mmap truncpanics the kernel on its firstiteration, in every run that reached it (3 of 3):
(serial COM2 and VGA "System Panic" box; no backtrace printed). Kernel:
mach_kernel.issue43(cksum 1562297808, builtOct 15, ffs+hfs+ext2fs+msdos, DIAGNOSTIC). The same test passes on UFS (/tmp)
and on HFS+, and
stress-mmap shared mixed privatepass on ext2.After the panic,
e2fsck -fnon the volume reports only leaked blocks(block bitmap differences, wrong free counts); no inode or directory damage.
Reproduce
i386 QEMU guest, ext2 rev 1 volume (1 KiB blocks, 128-byte inodes, features
filetype,sparse_super, made bymke2fs -t ext2 -r 1 -O none,filetype,sparse_super -I 128 -b 1024)wrapped by
vm/ext2_guest.py wrap_volume, mounted withmount_ext2fs /dev/hd2a /mnt/e. Then:which does, in one process:
open(O_RDWR|O_CREAT|O_TRUNC),ftruncate(fd, 64 KiB)(sparse, no blocks)mmap(64 KiB, MAP_SHARED)and write every byte of all 16 pagesftruncate(fd, 20 KiB)<- panics hereWith 1 KiB blocks, pages 3..4 (bytes 12..20 KiB) map through the single
indirect block, which does not exist yet when the truncate starts.
Artifacts:
vm/work/fsrepro/ext2-1-panic.{ppm,png,log}(copy ofext2-trunc-n1i1-panic.*),ext2-mmap-panic.{png,log}(first hit, fromstress-mmap -n 3 -i 20 shared mixed private trunc),ext2-after-panic-fsck.txt.Where to look
bsd/ext2fs/ext2fs_inode.c,ext2fs_truncate(): the DIAGNOSTIC check afterdone:findsnewblks[NDADDR+SINGLE] != e2di_blocks[NDADDR+SINGLE], so thesingle-indirect pointer changed between the
VOP_UPDATEthat savednewblksand the end of the truncate. In that window the old blockpointers and
osizeare put back andvinvalbuf(V_SAVE)runs. The likeliestcause is the dirty mapped pages being pushed (pageout through
ufs_pageout/VOP_WRITE/ext2fs_balloc) while the inode looks like the old 64 KiB file:the push allocates the missing single-indirect block, so the pointer goes from
0 to a new block.
ext2fs_vm_flush(..., truncate=1, length)/mapfs_truncrunsbefore this and does not seem to stop it, perhaps because the pages below
lengthare pushed asynchronously, or because a push happens again fromvinvalbuf. Compareffs_truncate, which passes the same test and also starts withmapfs_trunc. On a kernel without DIAGNOSTIC, the same race would most likelyleave the inode pointing at a block that was allocated during the truncate,
or leak it.
Found by the stress suite (
src/Commands/stress, branchstress-phase3) during the phase 3 filesystem run. Related: #45 (rename races, fixed on fs-races).@raynorpat commented on GitHub (Oct 16, 2026):
Fixed in
260033acbb(kernel: Merge filesystem repairs for issues 52-55), on ffs-perf; the ext2fs part is inext2fs_vnops.c.Verified on the repair kernel:
stress-mmap -n 1 -i 1 -s 1 -d /mnt/e truncno longer panics.vm/ext2_mmap_truncate_test.c(dirty sparse mappings shrunk across direct/indirect block boundaries, partial-page EOF, truncate to zero, zero-filled regrowth) passes at all four lengths.stress-mmap shared mixed private trunc -n 3 -i 20 -s 2passes on ext2.e2fsck -fnis clean afterwards.Test notes and focused regressions:
vm/filesystem-issues-52-55.mdin that commit. Repair kernel cksum 3500498149 (i386, QEMU snapshot); PowerPC not tested.