ext4: verify dir block before splitting it
commit 46c116b920
upstream.
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>
Link: https://lore.kernel.org/r/20220518093332.13986-1-jack@suse.cz
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
95a15bf999
commit
e5af1e10fe
|
@ -273,9 +273,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(char *from, char *to,
|
||||
struct dx_map_entry *offsets, int count, unsigned blocksize);
|
||||
|
@ -1205,15 +1205,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) {
|
||||
ext4fs_dirhash(dir, de->name, de->name_len, &h);
|
||||
map_tail--;
|
||||
|
@ -1223,8 +1231,7 @@ 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);
|
||||
de = ext4_next_entry(de, dir->i_sb->s_blocksize);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -1848,8 +1855,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 */
|
||||
|
|
Loading…
Reference in New Issue