Re: [Bug report] ext4: mount-time orphan recovery silently truncates a LIVE file (crafted s_last_orphan; only log line is the routine "1 truncate cleaned up")
From: Jan Kara
Date: Tue Oct 13 2026 - 10:28:25 EST
Hi!
On Tue 13-10-26 19:51:51, yangguikun wrote:
> During field-semantic corruption testing of ext4 images, we found that the
> mount-time orphan recovery path truncates a fully-live, linked file when
> four individually-plausible on-disk fields are made mutually inconsistent —
> and the only kernel log line is the routine recovery message, making the
> destruction indistinguishable from legitimate crash recovery. Deterministic
> reproducer below (e2fsprogs + one mount; no syscalls needed after mount).
>
> ## The corruption
>
> Four coordinated edits on a stock 1MB ext4 (mke2fs -O ^has_journal,^metadata_csum,
> three 8KB files; /file1 is a normal live file, nlink=1, has a dirent):
>
> 1. s_last_orphan (sb+0xE8) := /file1's inode number
> 2. /file1 i_dtime := 0
> 3. s_state: clear EXT4_VALID_FS
> 4. /file1 i_size := 4096 (real data: 8192)
>
...
> ## Why we think this is worth a look beyond "corrupt fs"
>
> - The damage is cross-file and kernel-initiated: one superblock u32 selects
> ANY inode on the filesystem for data destruction at mount, with zero
> syscalls from the (unprivileged-after-mount) point of view.
> - It is silent: the only log line ("N truncate cleaned up") is exactly what
> legitimate recovery prints. An operator mounting a suspect image gets no
> signal that a live file was destroyed.
> - The comment's "Having the inode bit set should be a 100% indicator that
> this is a valid orphan" holds only if the inode bitmap is trustworthy;
> with a crafted image both the bitmap bit and the chain head are
> attacker-chosen, and the no-nlink-check design decision becomes the
> amplification point.
I'm sorry but all I can read here is: "When you corrupt a filesystem, you can
silently loose data" which isn't really surprising and I don't really care
much.
> We appreciate the truncate-in-flight orphan use case that motivates
> skipping the nlink check, so a full "must be unlinked" rule is not the
> answer. Two directions that might be cheap enough:
>
> a) warn loudly (ext4_warning with inode number and old/new size) whenever
> orphan cleanup truncates an inode with i_nlink > 0 — turning the
> silent destruction into an observable event, or
No. Trucation of inode with i_nlink > 0 is a perfectly valid event so
warning about it wrong.
> b) recording the truncated inodes (e.g. rate-limited per-mount summary
> "orphan cleanup truncated N linked inodes") so post-mount forensics
> can distinguish this from normal recovery.
But this is already reported. There's:
ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
nr_truncates, str_plural(nr_truncates));
so I'm not sure what else you're asking for...
Honza
--
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR