From: Theodore Ts'o tytso@mit.edu
[ Upstream commit 099b847ccc6c1ad2f805d13cfbcc83f5b6d4bc42 ]
A syzbot fuzzed image triggered a BUG_ON in ext4_update_inline_data() when an inode had the INLINE_DATA_FL flag set but was missing the system.data extended attribute.
Since this can happen due to a maiciouly fuzzed file system, we shouldn't BUG, but rather, report it as a corrupted file system.
Add similar replacements of BUG_ON with EXT4_ERROR_INODE() ii ext4_create_inline_data() and ext4_inline_data_truncate().
Reported-by: syzbot+544248a761451c0df72f@syzkaller.appspotmail.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
**Backport Status: YES**
This commit should be backported to stable kernel trees for the following reasons:
## Critical Bug Fix 1. **Fixes kernel BUG_ON crashes**: The commit replaces three BUG_ON() calls with proper error handling using EXT4_ERROR_INODE() and returning -EFSCORRUPTED. This prevents kernel panics when encountering corrupted filesystem metadata.
2. **Security/Stability improvement**: The issue was found by syzbot fuzzing, indicating it can be triggered by maliciously crafted filesystems. Converting BUG_ON to error handling prevents denial-of- service attacks where a corrupted filesystem image could crash the entire system.
## Meets Stable Criteria 1. **Small and contained fix**: The changes are minimal - only replacing BUG_ON statements with proper error checks in three locations within fs/ext4/inline.c: - Line 306 in ext4_create_inline_data() - Line 357 in ext4_update_inline_data() - Line 1871 in ext4_inline_data_truncate()
2. **Clear bug fix without new features**: This is purely a bug fix that handles filesystem corruption gracefully instead of crashing. No new functionality is added.
3. **Low regression risk**: The change follows established patterns in ext4 - the file already uses EXT4_ERROR_INODE() and -EFSCORRUPTED in similar situations (line 168-170). The error handling is consistent with how ext4 handles corruption elsewhere in the codebase.
4. **Important for users**: Any user mounting potentially corrupted ext4 filesystems (from damaged media, malicious sources, or fuzzing) could encounter these BUG_ONs. Converting them to proper error handling significantly improves system stability.
## Code Analysis The patch correctly: - Replaces `BUG_ON(!is.s.not_found)` with a check that reports corruption and returns -EFSCORRUPTED when the xattr should be absent but is found - Replaces `BUG_ON(is.s.not_found)` with a check that reports corruption when the xattr should exist but is missing - Uses the same error reporting pattern (EXT4_ERROR_INODE) that ext4 uses throughout for filesystem corruption cases - Properly propagates the error up the call chain via the existing error handling paths
This is a textbook example of a stable-worthy fix: it addresses a real crash issue found by fuzzing, uses minimal changes, follows existing code patterns, and significantly improves system reliability when dealing with corrupted filesystems.
fs/ext4/inline.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index a1bbcdf40824..fa6668126fd7 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -303,7 +303,11 @@ static int ext4_create_inline_data(handle_t *handle, if (error) goto out;
- BUG_ON(!is.s.not_found); + if (!is.s.not_found) { + EXT4_ERROR_INODE(inode, "unexpected inline data xattr"); + error = -EFSCORRUPTED; + goto out; + }
error = ext4_xattr_ibody_set(handle, inode, &i, &is); if (error) { @@ -354,7 +358,11 @@ static int ext4_update_inline_data(handle_t *handle, struct inode *inode, if (error) goto out;
- BUG_ON(is.s.not_found); + if (is.s.not_found) { + EXT4_ERROR_INODE(inode, "missing inline data xattr"); + error = -EFSCORRUPTED; + goto out; + }
len -= EXT4_MIN_INLINE_DATA_SIZE; value = kzalloc(len, GFP_NOFS); @@ -1903,7 +1911,12 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline) if ((err = ext4_xattr_ibody_find(inode, &i, &is)) != 0) goto out_error;
- BUG_ON(is.s.not_found); + if (is.s.not_found) { + EXT4_ERROR_INODE(inode, + "missing inline data xattr"); + err = -EFSCORRUPTED; + goto out_error; + }
value_len = le32_to_cpu(is.s.here->e_value_size); value = kmalloc(value_len, GFP_NOFS);