Skip to content

Commit 3f9eafe

Browse files
tytsogregkh
authored andcommitted
ext4: avoid divide by zero fault when deleting corrupted inline directories
commit 4d982e2 upstream. A specially crafted file system can trick empty_inline_dir() into reading past the last valid entry in a inline directory, and then run into the end of xattr marker. This will trigger a divide by zero fault. Fix this by using the size of the inline directory instead of dir->i_size. Also clean up error reporting in __ext4_check_dir_entry so that the message is clearer and more understandable --- and avoids the division by zero trap if the size passed in is zero. (I'm not sure why we coded it that way in the first place; printing offset % size is actually more confusing and less useful.) https://bugzilla.kernel.org/show_bug.cgi?id=200933 Signed-off-by: Theodore Ts'o <tytso@mit.edu> Reported-by: Wen Xu <wen.xu@gatech.edu> Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 31343d2 commit 3f9eafe

2 files changed

Lines changed: 12 additions & 12 deletions

File tree

fs/ext4/dir.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
7575
else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
7676
error_msg = "rec_len is too small for name_len";
7777
else if (unlikely(((char *) de - buf) + rlen > size))
78-
error_msg = "directory entry across range";
78+
error_msg = "directory entry overrun";
7979
else if (unlikely(le32_to_cpu(de->inode) >
8080
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
8181
error_msg = "inode out of bounds";
@@ -84,18 +84,16 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
8484

8585
if (filp)
8686
ext4_error_file(filp, function, line, bh->b_blocknr,
87-
"bad entry in directory: %s - offset=%u(%u), "
88-
"inode=%u, rec_len=%d, name_len=%d",
89-
error_msg, (unsigned) (offset % size),
90-
offset, le32_to_cpu(de->inode),
91-
rlen, de->name_len);
87+
"bad entry in directory: %s - offset=%u, "
88+
"inode=%u, rec_len=%d, name_len=%d, size=%d",
89+
error_msg, offset, le32_to_cpu(de->inode),
90+
rlen, de->name_len, size);
9291
else
9392
ext4_error_inode(dir, function, line, bh->b_blocknr,
94-
"bad entry in directory: %s - offset=%u(%u), "
95-
"inode=%u, rec_len=%d, name_len=%d",
96-
error_msg, (unsigned) (offset % size),
97-
offset, le32_to_cpu(de->inode),
98-
rlen, de->name_len);
93+
"bad entry in directory: %s - offset=%u, "
94+
"inode=%u, rec_len=%d, name_len=%d, size=%d",
95+
error_msg, offset, le32_to_cpu(de->inode),
96+
rlen, de->name_len, size);
9997

10098
return 1;
10199
}

fs/ext4/inline.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1759,6 +1759,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
17591759
{
17601760
int err, inline_size;
17611761
struct ext4_iloc iloc;
1762+
size_t inline_len;
17621763
void *inline_pos;
17631764
unsigned int offset;
17641765
struct ext4_dir_entry_2 *de;
@@ -1786,8 +1787,9 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
17861787
goto out;
17871788
}
17881789

1790+
inline_len = ext4_get_inline_size(dir);
17891791
offset = EXT4_INLINE_DOTDOT_SIZE;
1790-
while (offset < dir->i_size) {
1792+
while (offset < inline_len) {
17911793
de = ext4_get_inline_entry(dir, &iloc, offset,
17921794
&inline_pos, &inline_size);
17931795
if (ext4_check_dir_entry(dir, NULL, de,

0 commit comments

Comments
 (0)