Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83a27745 authored by Theodore Ts'o's avatar Theodore Ts'o Committed by Greg Kroah-Hartman
Browse files

ext4: avoid divide by zero fault when deleting corrupted inline directories

commit 4d982e25d0bdc83d8c64e66fdeca0b89240b3b85 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: default avatarTheodore Ts'o <tytso@mit.edu>
Reported-by: default avatarWen Xu <wen.xu@gatech.edu>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 632ff8fc
Loading
Loading
Loading
Loading
+9 −11
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
	else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
		error_msg = "rec_len is too small for name_len";
	else if (unlikely(((char *) de - buf) + rlen > size))
		error_msg = "directory entry across range";
		error_msg = "directory entry overrun";
	else if (unlikely(le32_to_cpu(de->inode) >
			le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
		error_msg = "inode out of bounds";
@@ -86,18 +86,16 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,

	if (filp)
		ext4_error_file(filp, function, line, bh->b_blocknr,
				"bad entry in directory: %s - offset=%u(%u), "
				"inode=%u, rec_len=%d, name_len=%d",
				error_msg, (unsigned) (offset % size),
				offset, le32_to_cpu(de->inode),
				rlen, de->name_len);
				"bad entry in directory: %s - offset=%u, "
				"inode=%u, rec_len=%d, name_len=%d, size=%d",
				error_msg, offset, le32_to_cpu(de->inode),
				rlen, de->name_len, size);
	else
		ext4_error_inode(dir, function, line, bh->b_blocknr,
				"bad entry in directory: %s - offset=%u(%u), "
				"inode=%u, rec_len=%d, name_len=%d",
				error_msg, (unsigned) (offset % size),
				offset, le32_to_cpu(de->inode),
				rlen, de->name_len);
				"bad entry in directory: %s - offset=%u, "
				"inode=%u, rec_len=%d, name_len=%d, size=%d",
				 error_msg, offset, le32_to_cpu(de->inode),
				 rlen, de->name_len, size);

	return 1;
}
+3 −1
Original line number Diff line number Diff line
@@ -1738,6 +1738,7 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data)
{
	int err, inline_size;
	struct ext4_iloc iloc;
	size_t inline_len;
	void *inline_pos;
	unsigned int offset;
	struct ext4_dir_entry_2 *de;
@@ -1765,8 +1766,9 @@ int empty_inline_dir(struct inode *dir, int *has_inline_data)
		goto out;
	}

	inline_len = ext4_get_inline_size(dir);
	offset = EXT4_INLINE_DOTDOT_SIZE;
	while (offset < dir->i_size) {
	while (offset < inline_len) {
		de = ext4_get_inline_entry(dir, &iloc, offset,
					   &inline_pos, &inline_size);
		if (ext4_check_dir_entry(dir, NULL, de,