4.4-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Theodore Ts'o <ty...@mit.edu>

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: Theodore Ts'o <ty...@mit.edu>
Reported-by: Wen Xu <wen...@gatech.edu>
Cc: sta...@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 fs/ext4/dir.c    |   20 +++++++++-----------
 fs/ext4/inline.c |    4 +++-
 2 files changed, 12 insertions(+), 12 deletions(-)

--- a/fs/ext4/dir.c
+++ b/fs/ext4/dir.c
@@ -74,7 +74,7 @@ int __ext4_check_dir_entry(const char *f
        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";
@@ -83,18 +83,16 @@ int __ext4_check_dir_entry(const char *f
 
        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;
 }
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1756,6 +1756,7 @@ int empty_inline_dir(struct inode *dir,
 {
        int err, inline_size;
        struct ext4_iloc iloc;
+       size_t inline_len;
        void *inline_pos;
        unsigned int offset;
        struct ext4_dir_entry_2 *de;
@@ -1783,8 +1784,9 @@ int empty_inline_dir(struct inode *dir,
                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,


Reply via email to