Jan Kara fff6de
From 4ea99936a1630f51fc3a2d61a58ec4a1c4b7d55a Mon Sep 17 00:00:00 2001
Jan Kara fff6de
From: Theodore Ts'o <tytso@mit.edu>
Jan Kara fff6de
Date: Thu, 7 Nov 2019 21:43:41 -0500
Jan Kara fff6de
Subject: [PATCH] ext4: add more paranoia checking in ext4_expand_extra_isize
Jan Kara fff6de
 handling
Jan Kara fff6de
Git-commit: 4ea99936a1630f51fc3a2d61a58ec4a1c4b7d55a
Jan Kara fff6de
Patch-mainline: v5.5-rc1
Jan Kara fff6de
References: bsc#1159297 CVE-2019-19767
Jan Kara fff6de
Jan Kara fff6de
It's possible to specify a non-zero s_want_extra_isize via debugging
Jan Kara fff6de
option, and this can cause bad things(tm) to happen when using a file
Jan Kara fff6de
system with an inode size of 128 bytes.
Jan Kara fff6de
Jan Kara fff6de
Add better checking when the file system is mounted, as well as when
Jan Kara fff6de
we are actually doing the trying to do the inode expansion.
Jan Kara fff6de
Jan Kara fff6de
Link: https://lore.kernel.org/r/20191110121510.GH23325@mit.edu
Jan Kara fff6de
Reported-by: syzbot+f8d6f8386ceacdbfff57@syzkaller.appspotmail.com
Jan Kara fff6de
Reported-by: syzbot+33d7ea72e47de3bdf4e1@syzkaller.appspotmail.com
Jan Kara fff6de
Reported-by: syzbot+44b6763edfc17144296f@syzkaller.appspotmail.com
Jan Kara fff6de
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Jan Kara fff6de
Cc: stable@kernel.org
Jan Kara fff6de
Acked-by: Jan Kara <jack@suse.cz>
Jan Kara fff6de
Jan Kara fff6de
---
Jan Kara fff6de
 fs/ext4/inode.c |   15 +++++++++++++++
Jan Kara fff6de
 fs/ext4/super.c |   21 ++++++++++++---------
Jan Kara fff6de
 2 files changed, 27 insertions(+), 9 deletions(-)
Jan Kara fff6de
Jan Kara fff6de
--- a/fs/ext4/inode.c
Jan Kara fff6de
+++ b/fs/ext4/inode.c
Jan Kara fff6de
@@ -5736,6 +5736,21 @@ static int ext4_expand_extra_isize(struc
Jan Kara fff6de
 {
Jan Kara fff6de
 	struct ext4_inode *raw_inode;
Jan Kara fff6de
 	struct ext4_xattr_ibody_header *header;
Jan Kara fff6de
+	unsigned int inode_size = EXT4_INODE_SIZE(inode->i_sb);
Jan Kara fff6de
+	struct ext4_inode_info *ei = EXT4_I(inode);
Jan Kara fff6de
+
Jan Kara fff6de
+	/* this was checked at iget time, but double check for good measure */
Jan Kara fff6de
+	if ((EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > inode_size) ||
Jan Kara fff6de
+	    (ei->i_extra_isize & 3)) {
Jan Kara fff6de
+		EXT4_ERROR_INODE(inode, "bad extra_isize %u (inode size %u)",
Jan Kara fff6de
+				 ei->i_extra_isize,
Jan Kara fff6de
+				 EXT4_INODE_SIZE(inode->i_sb));
Jan Kara fff6de
+		return -EFSCORRUPTED;
Jan Kara fff6de
+	}
Jan Kara fff6de
+	if ((new_extra_isize < ei->i_extra_isize) ||
Jan Kara fff6de
+	    (new_extra_isize < 4) ||
Jan Kara fff6de
+	    (new_extra_isize > inode_size - EXT4_GOOD_OLD_INODE_SIZE))
Jan Kara fff6de
+		return -EINVAL;	/* Should never happen */
Jan Kara fff6de
 
Jan Kara fff6de
 	if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
Jan Kara fff6de
 		return 0;
Jan Kara fff6de
--- a/fs/ext4/super.c
Jan Kara fff6de
+++ b/fs/ext4/super.c
Jan Kara fff6de
@@ -3429,12 +3429,15 @@ static void ext4_clamp_want_extra_isize(
Jan Kara fff6de
 {
Jan Kara fff6de
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
Jan Kara fff6de
 	struct ext4_super_block *es = sbi->s_es;
Jan Kara fff6de
+	unsigned def_extra_isize = sizeof(struct ext4_inode) -
Jan Kara fff6de
+						EXT4_GOOD_OLD_INODE_SIZE;
Jan Kara fff6de
 
Jan Kara fff6de
-	/* determine the minimum size of new large inodes, if present */
Jan Kara fff6de
-	if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE &&
Jan Kara fff6de
-	    sbi->s_want_extra_isize == 0) {
Jan Kara fff6de
-		sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
Jan Kara fff6de
-						     EXT4_GOOD_OLD_INODE_SIZE;
Jan Kara fff6de
+	if (sbi->s_inode_size == EXT4_GOOD_OLD_INODE_SIZE) {
Jan Kara fff6de
+		sbi->s_want_extra_isize = 0;
Jan Kara fff6de
+		return;
Jan Kara fff6de
+	}
Jan Kara fff6de
+	if (sbi->s_want_extra_isize < 4) {
Jan Kara fff6de
+		sbi->s_want_extra_isize = def_extra_isize;
Jan Kara fff6de
 		if (ext4_has_feature_extra_isize(sb)) {
Jan Kara fff6de
 			if (sbi->s_want_extra_isize <
Jan Kara fff6de
 			    le16_to_cpu(es->s_want_extra_isize))
Jan Kara fff6de
@@ -3447,10 +3450,10 @@ static void ext4_clamp_want_extra_isize(
Jan Kara fff6de
 		}
Jan Kara fff6de
 	}
Jan Kara fff6de
 	/* Check if enough inode space is available */
Jan Kara fff6de
-	if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
Jan Kara fff6de
-							sbi->s_inode_size) {
Jan Kara fff6de
-		sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
Jan Kara fff6de
-						       EXT4_GOOD_OLD_INODE_SIZE;
Jan Kara fff6de
+	if ((sbi->s_want_extra_isize > sbi->s_inode_size) ||
Jan Kara fff6de
+	    (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
Jan Kara fff6de
+							sbi->s_inode_size)) {
Jan Kara fff6de
+		sbi->s_want_extra_isize = def_extra_isize;
Jan Kara fff6de
 		ext4_msg(sb, KERN_INFO,
Jan Kara fff6de
 			 "required extra inode space not available");
Jan Kara fff6de
 	}