Jan Kara d10c0d
From c7df4a1ecb8579838ec8c56b2bb6a6716e974f37 Mon Sep 17 00:00:00 2001
Jan Kara d10c0d
From: Theodore Ts'o <tytso@mit.edu>
Jan Kara d10c0d
Date: Mon, 11 Nov 2019 22:18:13 -0500
Jan Kara d10c0d
Subject: [PATCH] ext4: work around deleting a file with i_nlink == 0 safely
Jan Kara d10c0d
Git-commit: c7df4a1ecb8579838ec8c56b2bb6a6716e974f37
Jan Kara d10c0d
Patch-mainline: v5.5-rc1
Jan Kara d10c0d
References: bsc#1158819 CVE-2019-19447
Jan Kara d10c0d
Jan Kara d10c0d
If the file system is corrupted such that a file's i_links_count is
Jan Kara d10c0d
too small, then it's possible that when unlinking that file, i_nlink
Jan Kara d10c0d
will already be zero.  Previously we were working around this kind of
Jan Kara d10c0d
corruption by forcing i_nlink to one; but we were doing this before
Jan Kara d10c0d
trying to delete the directory entry --- and if the file system is
Jan Kara d10c0d
corrupted enough that ext4_delete_entry() fails, then we exit with
Jan Kara d10c0d
i_nlink elevated, and this causes the orphan inode list handling to be
Jan Kara d10c0d
FUBAR'ed, such that when we unmount the file system, the orphan inode
Jan Kara d10c0d
list can get corrupted.
Jan Kara d10c0d
Jan Kara d10c0d
A better way to fix this is to simply skip trying to call drop_nlink()
Jan Kara d10c0d
if i_nlink is already zero, thus moving the check to the place where
Jan Kara d10c0d
it makes the most sense.
Jan Kara d10c0d
Jan Kara d10c0d
https://bugzilla.kernel.org/show_bug.cgi?id=205433
Jan Kara d10c0d
Jan Kara d10c0d
Link: https://lore.kernel.org/r/20191112032903.8828-1-tytso@mit.edu
Jan Kara d10c0d
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Jan Kara d10c0d
Cc: stable@kernel.org
Jan Kara d10c0d
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
Jan Kara d10c0d
Acked-by: Jan Kara <jack@suse.cz>
Jan Kara d10c0d
Jan Kara d10c0d
---
Jan Kara d10c0d
 fs/ext4/namei.c | 11 +++++------
Jan Kara d10c0d
 1 file changed, 5 insertions(+), 6 deletions(-)
Jan Kara d10c0d
Jan Kara d10c0d
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
Jan Kara d10c0d
index a67cae3c8ff5..a856997d87b5 100644
Jan Kara d10c0d
--- a/fs/ext4/namei.c
Jan Kara d10c0d
+++ b/fs/ext4/namei.c
Jan Kara d10c0d
@@ -3196,18 +3196,17 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
Jan Kara d10c0d
 	if (IS_DIRSYNC(dir))
Jan Kara d10c0d
 		ext4_handle_sync(handle);
Jan Kara d10c0d
 
Jan Kara d10c0d
-	if (inode->i_nlink == 0) {
Jan Kara d10c0d
-		ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
Jan Kara d10c0d
-				   dentry->d_name.len, dentry->d_name.name);
Jan Kara d10c0d
-		set_nlink(inode, 1);
Jan Kara d10c0d
-	}
Jan Kara d10c0d
 	retval = ext4_delete_entry(handle, dir, de, bh);
Jan Kara d10c0d
 	if (retval)
Jan Kara d10c0d
 		goto end_unlink;
Jan Kara d10c0d
 	dir->i_ctime = dir->i_mtime = current_time(dir);
Jan Kara d10c0d
 	ext4_update_dx_flag(dir);
Jan Kara d10c0d
 	ext4_mark_inode_dirty(handle, dir);
Jan Kara d10c0d
-	drop_nlink(inode);
Jan Kara d10c0d
+	if (inode->i_nlink == 0)
Jan Kara d10c0d
+		ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
Jan Kara d10c0d
+				   dentry->d_name.len, dentry->d_name.name);
Jan Kara d10c0d
+	else
Jan Kara d10c0d
+		drop_nlink(inode);
Jan Kara d10c0d
 	if (!inode->i_nlink)
Jan Kara d10c0d
 		ext4_orphan_add(handle, inode);
Jan Kara d10c0d
 	inode->i_ctime = current_time(inode);
Jan Kara d10c0d
-- 
Jan Kara d10c0d
2.16.4
Jan Kara d10c0d