Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not.
CC: stable@vger.kernel.org Signed-off-by: Jan Kara jack@suse.cz --- fs/ext4/namei.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 767b4bfe39c3..ca6ee9940599 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -277,9 +277,9 @@ static struct dx_frame *dx_probe(struct ext4_filename *fname, struct dx_hash_info *hinfo, struct dx_frame *frame); static void dx_release(struct dx_frame *frames); -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, - struct dx_map_entry map[]); +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, + struct dx_map_entry *map_tail); static void dx_sort_map(struct dx_map_entry *map, unsigned count); static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from, char *to, struct dx_map_entry *offsets, @@ -1249,15 +1249,23 @@ static inline int search_dirblock(struct buffer_head *bh, * Create map of hash values, offsets, and sizes, stored at end of block. * Returns number of entries mapped. */ -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, - unsigned blocksize, struct dx_hash_info *hinfo, +static int dx_make_map(struct inode *dir, struct buffer_head *bh, + struct dx_hash_info *hinfo, struct dx_map_entry *map_tail) { int count = 0; - char *base = (char *) de; + struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data; + unsigned int buflen = bh->b_size; + char *base = bh->b_data; struct dx_hash_info h = *hinfo;
- while ((char *) de < base + blocksize) { + if (ext4_has_metadata_csum(dir->i_sb)) + buflen -= sizeof(struct ext4_dir_entry_tail); + + while ((char *) de < base + buflen) { + if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen, + ((char *)de) - base)) + return -EFSCORRUPTED; if (de->name_len && de->inode) { if (ext4_hash_in_dirent(dir)) h.hash = EXT4_DIRENT_HASH(de); @@ -1270,7 +1278,6 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de, count++; cond_resched(); } - /* XXX: do we need to check rec_len == 0 case? -Chris */ de = ext4_next_entry(de, blocksize); } return count; @@ -1943,8 +1950,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
/* create map in the end of data2 block */ map = (struct dx_map_entry *) (data2 + blocksize); - count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1, - blocksize, hinfo, map); + count = dx_make_map(dir, *bh, hinfo, map); + if (count < 0) { + err = count; + goto journal_error; + } map -= count; dx_sort_map(map, count); /* Ensure that neither split block is over half full */
On Thu, Apr 28, 2022 at 08:31:37PM +0200, Jan Kara wrote:
Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not.
This commit fails to build due to an undefined variable. It's fixed with this hunk in the next patch, which needs to be brought back into this commit:
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5951e9bb348e..7286472e9558 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1278,7 +1278,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, count++; cond_resched(); } - de = ext4_next_entry(de, blocksize); + de = ext4_next_entry(de, dir->i_sb->s_blocksize); } return count; }
I was thinking about folding in this change and apply the patch with that change --- and I may yet do that --- but it looks like there's a bigger problem with this patch series, which is that it's causing a crash when running ext4/052 due to what appears to be a smashed stack. More about that in the reply to patch 2/2 of this series....
- Ted
On Tue 17-05-22 19:40:11, Theodore Ts'o wrote:
On Thu, Apr 28, 2022 at 08:31:37PM +0200, Jan Kara wrote:
Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not.
This commit fails to build due to an undefined variable. It's fixed with this hunk in the next patch, which needs to be brought back into this commit:
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5951e9bb348e..7286472e9558 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1278,7 +1278,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, count++; cond_resched(); }
de = ext4_next_entry(de, blocksize);
} return count;de = ext4_next_entry(de, dir->i_sb->s_blocksize);
}
I was thinking about folding in this change and apply the patch with that change --- and I may yet do that --- but it looks like there's a bigger problem with this patch series, which is that it's causing a crash when running ext4/052 due to what appears to be a smashed stack. More about that in the reply to patch 2/2 of this series....
Yup, I'll fix that. Thanks for catching this.
Honza
linux-stable-mirror@lists.linaro.org