Bug 200931
Summary: | use-after-free in ext4_put_super() | ||
---|---|---|---|
Product: | File System | Reporter: | Wen Xu (wen.xu) |
Component: | ext4 | Assignee: | fs_ext4 (fs_ext4) |
Status: | ASSIGNED --- | ||
Severity: | normal | CC: | 6201613047, tytso, wen.xu |
Priority: | P1 | ||
Hardware: | All | ||
OS: | Linux | ||
Kernel Version: | 4.18 | Subsystem: | |
Regression: | No | Bisected commit-id: | |
Attachments: |
The (compressed) crafted image which causes crash
poc simplified poc.c Simplified crafted image |
Description
Wen Xu
2018-08-25 05:11:07 UTC
Created attachment 278079 [details]
poc
Created attachment 278099 [details]
simplified poc.c
Created attachment 278101 [details]
Simplified crafted image
Side note: I understand that the fuzzing algorithm you are using creates increasingly complex poc.c programs and crafted file system images. The problem is that both have included increasing large amounts of clutter that make it much harder to root cause the failure. An interesting research idea: is there some way you could automate simplifying the poc.c file? I just kept on cutting down the program; and if the failure went away, I would add back that line(s). And if the failure remained, I would try removing the next line(s). Even a brute-force "try removing operation N; does it fail/succeed"; and just incremently trying to remove each operation, one at a time, to see if the failure goes a way or not, would save me a huge amount of time. A similar thing could be done for the image. For example, the corrupted resize inode (#7) in your last couple of images has always been a red herring, and it causes "e2fsck -fn poc.img" to abort. So the first thing I've done is to run 'debugfs -w -R "clri <7> poc.img' and see if the failure remains. So far, it always has. (And unless you are trying to call one of the online resize ioctls, it almost certainly will make no difference.) Incrementally removing corruptions by using "e2fsck -f poc.img" and then seeing whether or not the failure goes away or not would also be useful --- although that one is actually less of a bother for a human to do by hand. Slimming down the the poc.c does take a large amount of toil, and if you have some automation framework that could do that automatically, that would be a great time-saver for the kernel developer. (It's why I haven't had time to look at your bug reports; each one takes the better part of half a day to analyze, and I don't have that much free time.) The root cause of the problem is as follows. Setup: the file system has an inode, inode #16, that has a i_nlink of 1. However, there are two directory entries (with weird names for no good reason but it makes life more difficult for the bug hunter), which I will call file A and file B that both are hard links to inode #16. 1) Call chmod(File A, 3072); This brings Filename A into the dcache 2) Unlink File B. This drops the i_nlink to zero; since this means that there should be no remaining hardlinks to the file --- but i_count > 0, since we have a dcache entry for Filename A --- inode #16 is put on the orphan inode list. 3) Create a new file, call it File C. 4) Rename File C on top of File A. This causes a warning to get issued since there is an attempt to drop i_nlink to a negative value. That gets ignored so i_nlink stays at 0. But now i_count gets dropped down to 0, so the inode gets deleted and freed. But, the inode structure is still on the orphan inode list! 5) At unmount time, the fact that we still have an inode on the orphan inode list causes a warning to be printed, but it also causes the access to the freed data structure. The fix is to enforce a check that if new.inode exists, its i_nlink must be > 0. If it is 0, then something is badly wrong, and we should mark the file system as corrupted and return EFSCORRUPTED. The problem is obvious once we have the simplified poc. With the original poc, instead of 4 operations, there are 100 operations, most of which are complete red herrings. Patch to fix this can be found here: http://patchwork.ozlabs.org/patch/962339/ |