All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] ext4: clean up ea_inode handling
@ 2023-05-24  3:49 Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 1/4] ext4: add EA_INODE checking to ext4_iget() Theodore Ts'o
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-24  3:49 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

This fixes a number of problems with ea_inode handling which were
pointed out by syzbot.  The first and third add some additional
checking for invalid / maliciously fuzzed file systems.  The second
and fourth patch adds some lockdep annotations to avoid some false
positive reports from lockdep.

There is still one remaining syzbot report[1] relating to ea_inodes
not handled by this patch series, and that is an apparently deadlock
which happens when a kernel thread is freeing an ea_inode racing with
another thread which is trying to find the mbcache entry (presumably
with the intent of reusing it).  The problem is apparently hard to
reproduce; it's only been hit 4 times, and there is no C reproducer;
just a syzkaller reproducer.  So we'll leave that for another day/

[1] https://syzkaller.appspot.com/bug?extid=38e6635a03c83c76297a
    INFO: task hung in ext4_evict_ea_inode


Theodore Ts'o (4):
  ext4: add EA_INODE checking to ext4_iget()
  ext4: set lockdep subclass for the ea_inode in
    ext4_xattr_inode_cache_find()
  ext4: disallow ea_inodes with extended attributes
  ext4: add lockdep annotations for i_data_sem for ea_inode's

 fs/ext4/ext4.h  |  5 ++++-
 fs/ext4/inode.c | 34 +++++++++++++++++++++++++++++-----
 fs/ext4/xattr.c | 41 ++++++++++++-----------------------------
 3 files changed, 45 insertions(+), 35 deletions(-)

-- 
2.31.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] ext4: add EA_INODE checking to ext4_iget()
  2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
@ 2023-05-24  3:49 ` Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 2/4] ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find() Theodore Ts'o
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-24  3:49 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: Theodore Ts'o, syzbot+cbb68193bdb95af4340a,
	syzbot+62120febbd1ee3c3c860, syzbot+edce54daffee36421b4c, stable

Add a new flag, EXT4_IGET_EA_INODE which indicates whether the inode
is expected to have the EA_INODE flag or not.  If the flag is not
set/clear as expected, then fail the iget() operation and mark the
file system as corrupted.

This commit also makes the ext4_iget() always perform the
is_bad_inode() check even when the inode is already inode cache.  This
allows us to remove the is_bad_inode() check from the callers of
ext4_iget() in the ea_inode code.

Reported-by: syzbot+cbb68193bdb95af4340a@syzkaller.appspotmail.com
Reported-by: syzbot+62120febbd1ee3c3c860@syzkaller.appspotmail.com
Reported-by: syzbot+edce54daffee36421b4c@syzkaller.appspotmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
---
 fs/ext4/ext4.h  |  3 ++-
 fs/ext4/inode.c | 31 ++++++++++++++++++++++++++-----
 fs/ext4/xattr.c | 36 +++++++-----------------------------
 3 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 6948d673bba2..9525c52b78dc 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2901,7 +2901,8 @@ typedef enum {
 	EXT4_IGET_NORMAL =	0,
 	EXT4_IGET_SPECIAL =	0x0001, /* OK to iget a system inode */
 	EXT4_IGET_HANDLE = 	0x0002,	/* Inode # is from a handle */
-	EXT4_IGET_BAD =		0x0004  /* Allow to iget a bad inode */
+	EXT4_IGET_BAD =		0x0004, /* Allow to iget a bad inode */
+	EXT4_IGET_EA_INODE =	0x0008	/* Inode should contain an EA value */
 } ext4_iget_flags;
 
 extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ce5f21b6c2b3..258f3cbed347 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4641,6 +4641,21 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)
 		inode_set_iversion_queried(inode, val);
 }
 
+static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags)
+
+{
+	if (flags & EXT4_IGET_EA_INODE) {
+		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
+			return "missing EA_INODE flag";
+	} else {
+		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
+			return "unexpected EA_INODE flag";
+	}
+	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD))
+		return "unexpected bad inode w/o EXT4_IGET_BAD";
+	return NULL;
+}
+
 struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
 			  ext4_iget_flags flags, const char *function,
 			  unsigned int line)
@@ -4650,6 +4665,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
 	struct ext4_inode_info *ei;
 	struct ext4_super_block *es = EXT4_SB(sb)->s_es;
 	struct inode *inode;
+	const char *err_str;
 	journal_t *journal = EXT4_SB(sb)->s_journal;
 	long ret;
 	loff_t size;
@@ -4677,8 +4693,14 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
 	inode = iget_locked(sb, ino);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
-	if (!(inode->i_state & I_NEW))
+	if (!(inode->i_state & I_NEW)) {
+		if ((err_str = check_igot_inode(inode, flags)) != NULL) {
+			ext4_error_inode(inode, function, line, 0, err_str);
+			iput(inode);
+			return ERR_PTR(-EFSCORRUPTED);
+		}
 		return inode;
+	}
 
 	ei = EXT4_I(inode);
 	iloc.bh = NULL;
@@ -4944,10 +4966,9 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
 	if (IS_CASEFOLDED(inode) && !ext4_has_feature_casefold(inode->i_sb))
 		ext4_error_inode(inode, function, line, 0,
 				 "casefold flag without casefold feature");
-	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) {
-		ext4_error_inode(inode, function, line, 0,
-				 "bad inode without EXT4_IGET_BAD flag");
-		ret = -EUCLEAN;
+	if ((err_str = check_igot_inode(inode, flags)) != NULL) {
+		ext4_error_inode(inode, function, line, 0, err_str);
+		ret = -EFSCORRUPTED;
 		goto bad_inode;
 	}
 
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index dfc2e223bd10..a27208129a80 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -433,7 +433,7 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
 		return -EFSCORRUPTED;
 	}
 
-	inode = ext4_iget(parent->i_sb, ea_ino, EXT4_IGET_NORMAL);
+	inode = ext4_iget(parent->i_sb, ea_ino, EXT4_IGET_EA_INODE);
 	if (IS_ERR(inode)) {
 		err = PTR_ERR(inode);
 		ext4_error(parent->i_sb,
@@ -441,23 +441,6 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
 			   err);
 		return err;
 	}
-
-	if (is_bad_inode(inode)) {
-		ext4_error(parent->i_sb,
-			   "error while reading EA inode %lu is_bad_inode",
-			   ea_ino);
-		err = -EIO;
-		goto error;
-	}
-
-	if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
-		ext4_error(parent->i_sb,
-			   "EA inode %lu does not have EXT4_EA_INODE_FL flag",
-			    ea_ino);
-		err = -EINVAL;
-		goto error;
-	}
-
 	ext4_xattr_inode_set_class(inode);
 
 	/*
@@ -478,9 +461,6 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
 
 	*ea_inode = inode;
 	return 0;
-error:
-	iput(inode);
-	return err;
 }
 
 /* Remove entry from mbcache when EA inode is getting evicted */
@@ -1556,11 +1536,10 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
 
 	while (ce) {
 		ea_inode = ext4_iget(inode->i_sb, ce->e_value,
-				     EXT4_IGET_NORMAL);
-		if (!IS_ERR(ea_inode) &&
-		    !is_bad_inode(ea_inode) &&
-		    (EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL) &&
-		    i_size_read(ea_inode) == value_len &&
+				     EXT4_IGET_EA_INODE);
+		if (IS_ERR(ea_inode))
+			goto next_entry;
+		if (i_size_read(ea_inode) == value_len &&
 		    !ext4_xattr_inode_read(ea_inode, ea_data, value_len) &&
 		    !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data,
 						    value_len) &&
@@ -1570,9 +1549,8 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
 			kvfree(ea_data);
 			return ea_inode;
 		}
-
-		if (!IS_ERR(ea_inode))
-			iput(ea_inode);
+		iput(ea_inode);
+	next_entry:
 		ce = mb_cache_entry_find_next(ea_inode_cache, ce);
 	}
 	kvfree(ea_data);
-- 
2.31.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find()
  2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 1/4] ext4: add EA_INODE checking to ext4_iget() Theodore Ts'o
@ 2023-05-24  3:49 ` Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 3/4] ext4: disallow ea_inodes with extended attributes Theodore Ts'o
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-24  3:49 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: Theodore Ts'o, stable, syzbot+d4b971e744b1f5439336

If the ea_inode has been pushed out of the inode cache while there is
still a reference in the mb_cache, the lockdep subclass will not be
set on the inode, which can lead to some lockdep false positives.

Fixes: 33d201e0277b ("xt4: fix lockdep warning about recursive inode locking")
Cc: stable@kernel.org
Reported-by: syzbot+d4b971e744b1f5439336@syzkaller.appspotmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/xattr.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index a27208129a80..ff7ab63c5b4f 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1539,6 +1539,7 @@ ext4_xattr_inode_cache_find(struct inode *inode, const void *value,
 				     EXT4_IGET_EA_INODE);
 		if (IS_ERR(ea_inode))
 			goto next_entry;
+		ext4_xattr_inode_set_class(ea_inode);
 		if (i_size_read(ea_inode) == value_len &&
 		    !ext4_xattr_inode_read(ea_inode, ea_data, value_len) &&
 		    !ext4_xattr_inode_verify_hashes(ea_inode, NULL, ea_data,
-- 
2.31.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] ext4: disallow ea_inodes with extended attributes
  2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 1/4] ext4: add EA_INODE checking to ext4_iget() Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 2/4] ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find() Theodore Ts'o
@ 2023-05-24  3:49 ` Theodore Ts'o
  2023-05-24  3:49 ` [PATCH 4/4] ext4: add lockdep annotations for i_data_sem for ea_inode's Theodore Ts'o
  2023-05-30 19:26 ` [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
  4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-24  3:49 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: Theodore Ts'o, stable, syzbot+e44749b6ba4d0434cd47

An ea_inode stores the value of an extended attribute; it can not have
extended attributes itself, or this will cause recursive nightmares.
Add a check in ext4_iget() to make sure this is the case.

Cc: stable@kernel.org
Reported-by: syzbot+e44749b6ba4d0434cd47@syzkaller.appspotmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/inode.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 258f3cbed347..02de439bf1f0 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4647,6 +4647,9 @@ static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags)
 	if (flags & EXT4_IGET_EA_INODE) {
 		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
 			return "missing EA_INODE flag";
+		if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
+		    EXT4_I(inode)->i_file_acl)
+			return "ea_inode with extended attributes";
 	} else {
 		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
 			return "unexpected EA_INODE flag";
-- 
2.31.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] ext4: add lockdep annotations for i_data_sem for ea_inode's
  2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
                   ` (2 preceding siblings ...)
  2023-05-24  3:49 ` [PATCH 3/4] ext4: disallow ea_inodes with extended attributes Theodore Ts'o
@ 2023-05-24  3:49 ` Theodore Ts'o
  2023-05-30 19:26 ` [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
  4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-24  3:49 UTC (permalink / raw)
  To: Ext4 Developers List
  Cc: Theodore Ts'o, stable, syzbot+298c5d8fb4a128bc27b0

Treat i_data_sem for ea_inodes as being in their own lockdep class to
avoid lockdep complaints about ext4_setattr's use of inode_lock() on
normal inodes potentially causing lock ordering with i_data_sem on
ea_inodes in ext4_xattr_inode_write().  However, ea_inodes will be
operated on by ext4_setattr(), so this isn't a problem.

Cc: stable@kernel.org
Link: https://syzkaller.appspot.com/bug?extid=298c5d8fb4a128bc27b0
Reported-by: syzbot+298c5d8fb4a128bc27b0@syzkaller.appspotmail.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  | 2 ++
 fs/ext4/xattr.c | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9525c52b78dc..8104a21b001a 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -918,11 +918,13 @@ do {									       \
  *			  where the second inode has larger inode number
  *			  than the first
  *  I_DATA_SEM_QUOTA  - Used for quota inodes only
+ *  I_DATA_SEM_EA     - Used for ea_inodes only
  */
 enum {
 	I_DATA_SEM_NORMAL = 0,
 	I_DATA_SEM_OTHER,
 	I_DATA_SEM_QUOTA,
+	I_DATA_SEM_EA
 };
 
 
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index ff7ab63c5b4f..13d7f17a9c8c 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -121,7 +121,11 @@ ext4_expand_inode_array(struct ext4_xattr_inode_array **ea_inode_array,
 #ifdef CONFIG_LOCKDEP
 void ext4_xattr_inode_set_class(struct inode *ea_inode)
 {
+	struct ext4_inode_info *ei = EXT4_I(ea_inode);
+
 	lockdep_set_subclass(&ea_inode->i_rwsem, 1);
+	(void) ei;	/* shut up clang warning if !CONFIG_LOCKDEP */
+	lockdep_set_subclass(&ei->i_data_sem, I_DATA_SEM_EA);
 }
 #endif
 
-- 
2.31.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] ext4: clean up ea_inode handling
  2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
                   ` (3 preceding siblings ...)
  2023-05-24  3:49 ` [PATCH 4/4] ext4: add lockdep annotations for i_data_sem for ea_inode's Theodore Ts'o
@ 2023-05-30 19:26 ` Theodore Ts'o
  4 siblings, 0 replies; 6+ messages in thread
From: Theodore Ts'o @ 2023-05-30 19:26 UTC (permalink / raw)
  To: Ext4 Developers List, Theodore Ts'o


On Tue, 23 May 2023 23:49:47 -0400, Theodore Ts'o wrote:
> This fixes a number of problems with ea_inode handling which were
> pointed out by syzbot.  The first and third add some additional
> checking for invalid / maliciously fuzzed file systems.  The second
> and fourth patch adds some lockdep annotations to avoid some false
> positive reports from lockdep.
> 
> There is still one remaining syzbot report[1] relating to ea_inodes
> not handled by this patch series, and that is an apparently deadlock
> which happens when a kernel thread is freeing an ea_inode racing with
> another thread which is trying to find the mbcache entry (presumably
> with the intent of reusing it).  The problem is apparently hard to
> reproduce; it's only been hit 4 times, and there is no C reproducer;
> just a syzkaller reproducer.  So we'll leave that for another day/
> 
> [...]

Applied, thanks!

[1/4] ext4: add EA_INODE checking to ext4_iget()
      commit: b3e6bcb94590dea45396b9481e47b809b1be4afa
[2/4] ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find()
      commit: d08927b3e89fde1b224d22d2bddcb8dc4fe616db
[3/4] ext4: disallow ea_inodes with extended attributes
      commit: 1e0e51238f151e26ccd0a8bd5f5cf32e85c19ac3
[4/4] ext4: add lockdep annotations for i_data_sem for ea_inode's
      commit: f901459a1f277ed921e255d4c3d54485769f7dbd

Best regards,
-- 
Theodore Ts'o <tytso@mit.edu>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-05-30 19:26 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-24  3:49 [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o
2023-05-24  3:49 ` [PATCH 1/4] ext4: add EA_INODE checking to ext4_iget() Theodore Ts'o
2023-05-24  3:49 ` [PATCH 2/4] ext4: set lockdep subclass for the ea_inode in ext4_xattr_inode_cache_find() Theodore Ts'o
2023-05-24  3:49 ` [PATCH 3/4] ext4: disallow ea_inodes with extended attributes Theodore Ts'o
2023-05-24  3:49 ` [PATCH 4/4] ext4: add lockdep annotations for i_data_sem for ea_inode's Theodore Ts'o
2023-05-30 19:26 ` [PATCH 0/4] ext4: clean up ea_inode handling Theodore Ts'o

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.