Skip to content

Commit 46c116b

Browse files
jankaratytso
authored andcommitted
ext4: verify dir block before splitting it
Before splitting a directory block verify its directory entries are sane so that the splitting code does not access memory it should not. Cc: [email protected] Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent c878bea commit 46c116b

File tree

1 file changed

+21
-11
lines changed

1 file changed

+21
-11
lines changed

fs/ext4/namei.c

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ static struct dx_frame *dx_probe(struct ext4_filename *fname,
277277
struct dx_hash_info *hinfo,
278278
struct dx_frame *frame);
279279
static void dx_release(struct dx_frame *frames);
280-
static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
281-
unsigned blocksize, struct dx_hash_info *hinfo,
282-
struct dx_map_entry map[]);
280+
static int dx_make_map(struct inode *dir, struct buffer_head *bh,
281+
struct dx_hash_info *hinfo,
282+
struct dx_map_entry *map_tail);
283283
static void dx_sort_map(struct dx_map_entry *map, unsigned count);
284284
static struct ext4_dir_entry_2 *dx_move_dirents(struct inode *dir, char *from,
285285
char *to, struct dx_map_entry *offsets,
@@ -1249,15 +1249,23 @@ static inline int search_dirblock(struct buffer_head *bh,
12491249
* Create map of hash values, offsets, and sizes, stored at end of block.
12501250
* Returns number of entries mapped.
12511251
*/
1252-
static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
1253-
unsigned blocksize, struct dx_hash_info *hinfo,
1252+
static int dx_make_map(struct inode *dir, struct buffer_head *bh,
1253+
struct dx_hash_info *hinfo,
12541254
struct dx_map_entry *map_tail)
12551255
{
12561256
int count = 0;
1257-
char *base = (char *) de;
1257+
struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data;
1258+
unsigned int buflen = bh->b_size;
1259+
char *base = bh->b_data;
12581260
struct dx_hash_info h = *hinfo;
12591261

1260-
while ((char *) de < base + blocksize) {
1262+
if (ext4_has_metadata_csum(dir->i_sb))
1263+
buflen -= sizeof(struct ext4_dir_entry_tail);
1264+
1265+
while ((char *) de < base + buflen) {
1266+
if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen,
1267+
((char *)de) - base))
1268+
return -EFSCORRUPTED;
12611269
if (de->name_len && de->inode) {
12621270
if (ext4_hash_in_dirent(dir))
12631271
h.hash = EXT4_DIRENT_HASH(de);
@@ -1270,8 +1278,7 @@ static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
12701278
count++;
12711279
cond_resched();
12721280
}
1273-
/* XXX: do we need to check rec_len == 0 case? -Chris */
1274-
de = ext4_next_entry(de, blocksize);
1281+
de = ext4_next_entry(de, dir->i_sb->s_blocksize);
12751282
}
12761283
return count;
12771284
}
@@ -1943,8 +1950,11 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
19431950

19441951
/* create map in the end of data2 block */
19451952
map = (struct dx_map_entry *) (data2 + blocksize);
1946-
count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1,
1947-
blocksize, hinfo, map);
1953+
count = dx_make_map(dir, *bh, hinfo, map);
1954+
if (count < 0) {
1955+
err = count;
1956+
goto journal_error;
1957+
}
19481958
map -= count;
19491959
dx_sort_map(map, count);
19501960
/* Ensure that neither split block is over half full */

0 commit comments

Comments
 (0)