Skip to content

Commit c29c0ae

Browse files
Alex Tomastytso
authored andcommitted
ext4: Make extents code sanely handle on-disk corruption
Add more run-time checking of extent header fields and remove BUG_ON checks so we don't panic the kernel just because the on-disk filesystem is corrupted. Signed-off-by: Alex Tomas <alex@clusterfs.com> Signed-off-by: Mingming Cao <cmm@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
1 parent ff9ddf7 commit c29c0ae

File tree

1 file changed

+86
-58
lines changed

1 file changed

+86
-58
lines changed

fs/ext4/extents.c

Lines changed: 86 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -92,36 +92,6 @@ static void ext4_idx_store_pblock(struct ext4_extent_idx *ix, ext4_fsblk_t pb)
9292
ix->ei_leaf_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) & 0xffff);
9393
}
9494

95-
static int ext4_ext_check_header(const char *function, struct inode *inode,
96-
struct ext4_extent_header *eh)
97-
{
98-
const char *error_msg = NULL;
99-
100-
if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) {
101-
error_msg = "invalid magic";
102-
goto corrupted;
103-
}
104-
if (unlikely(eh->eh_max == 0)) {
105-
error_msg = "invalid eh_max";
106-
goto corrupted;
107-
}
108-
if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) {
109-
error_msg = "invalid eh_entries";
110-
goto corrupted;
111-
}
112-
return 0;
113-
114-
corrupted:
115-
ext4_error(inode->i_sb, function,
116-
"bad header in inode #%lu: %s - magic %x, "
117-
"entries %u, max %u, depth %u",
118-
inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic),
119-
le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max),
120-
le16_to_cpu(eh->eh_depth));
121-
122-
return -EIO;
123-
}
124-
12595
static handle_t *ext4_ext_journal_restart(handle_t *handle, int needed)
12696
{
12797
int err;
@@ -270,6 +240,70 @@ static int ext4_ext_space_root_idx(struct inode *inode)
270240
return size;
271241
}
272242

243+
static int
244+
ext4_ext_max_entries(struct inode *inode, int depth)
245+
{
246+
int max;
247+
248+
if (depth == ext_depth(inode)) {
249+
if (depth == 0)
250+
max = ext4_ext_space_root(inode);
251+
else
252+
max = ext4_ext_space_root_idx(inode);
253+
} else {
254+
if (depth == 0)
255+
max = ext4_ext_space_block(inode);
256+
else
257+
max = ext4_ext_space_block_idx(inode);
258+
}
259+
260+
return max;
261+
}
262+
263+
static int __ext4_ext_check_header(const char *function, struct inode *inode,
264+
struct ext4_extent_header *eh,
265+
int depth)
266+
{
267+
const char *error_msg;
268+
int max = 0;
269+
270+
if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) {
271+
error_msg = "invalid magic";
272+
goto corrupted;
273+
}
274+
if (unlikely(le16_to_cpu(eh->eh_depth) != depth)) {
275+
error_msg = "unexpected eh_depth";
276+
goto corrupted;
277+
}
278+
if (unlikely(eh->eh_max == 0)) {
279+
error_msg = "invalid eh_max";
280+
goto corrupted;
281+
}
282+
max = ext4_ext_max_entries(inode, depth);
283+
if (unlikely(le16_to_cpu(eh->eh_max) > max)) {
284+
error_msg = "too large eh_max";
285+
goto corrupted;
286+
}
287+
if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) {
288+
error_msg = "invalid eh_entries";
289+
goto corrupted;
290+
}
291+
return 0;
292+
293+
corrupted:
294+
ext4_error(inode->i_sb, function,
295+
"bad header in inode #%lu: %s - magic %x, "
296+
"entries %u, max %u(%u), depth %u(%u)",
297+
inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic),
298+
le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max),
299+
max, le16_to_cpu(eh->eh_depth), depth);
300+
301+
return -EIO;
302+
}
303+
304+
#define ext4_ext_check_header(inode, eh, depth) \
305+
__ext4_ext_check_header(__FUNCTION__, inode, eh, depth)
306+
273307
#ifdef EXT_DEBUG
274308
static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
275309
{
@@ -330,16 +364,14 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path)
330364
/*
331365
* ext4_ext_binsearch_idx:
332366
* binary search for the closest index of the given block
367+
* the header must be checked before calling this
333368
*/
334369
static void
335370
ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int block)
336371
{
337372
struct ext4_extent_header *eh = path->p_hdr;
338373
struct ext4_extent_idx *r, *l, *m;
339374

340-
BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC);
341-
BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max));
342-
BUG_ON(le16_to_cpu(eh->eh_entries) <= 0);
343375

344376
ext_debug("binsearch for %d(idx): ", block);
345377

@@ -389,16 +421,14 @@ ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int bloc
389421
/*
390422
* ext4_ext_binsearch:
391423
* binary search for closest extent of the given block
424+
* the header must be checked before calling this
392425
*/
393426
static void
394427
ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block)
395428
{
396429
struct ext4_extent_header *eh = path->p_hdr;
397430
struct ext4_extent *r, *l, *m;
398431

399-
BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC);
400-
BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max));
401-
402432
if (eh->eh_entries == 0) {
403433
/*
404434
* this leaf is empty:
@@ -469,11 +499,10 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path)
469499
short int depth, i, ppos = 0, alloc = 0;
470500

471501
eh = ext_inode_hdr(inode);
472-
BUG_ON(eh == NULL);
473-
if (ext4_ext_check_header(__FUNCTION__, inode, eh))
502+
depth = ext_depth(inode);
503+
if (ext4_ext_check_header(inode, eh, depth))
474504
return ERR_PTR(-EIO);
475505

476-
i = depth = ext_depth(inode);
477506

478507
/* account possible depth increase */
479508
if (!path) {
@@ -485,10 +514,12 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path)
485514
}
486515
path[0].p_hdr = eh;
487516

517+
i = depth;
488518
/* walk through the tree */
489519
while (i) {
490520
ext_debug("depth %d: num %d, max %d\n",
491521
ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
522+
492523
ext4_ext_binsearch_idx(inode, path + ppos, block);
493524
path[ppos].p_block = idx_pblock(path[ppos].p_idx);
494525
path[ppos].p_depth = i;
@@ -505,7 +536,7 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path)
505536
path[ppos].p_hdr = eh;
506537
i--;
507538

508-
if (ext4_ext_check_header(__FUNCTION__, inode, eh))
539+
if (ext4_ext_check_header(inode, eh, i))
509540
goto err;
510541
}
511542

@@ -514,9 +545,6 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path)
514545
path[ppos].p_ext = NULL;
515546
path[ppos].p_idx = NULL;
516547

517-
if (ext4_ext_check_header(__FUNCTION__, inode, eh))
518-
goto err;
519-
520548
/* find extent */
521549
ext4_ext_binsearch(inode, path + ppos, block);
522550

@@ -1738,13 +1766,12 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
17381766
unsigned uninitialized = 0;
17391767
struct ext4_extent *ex;
17401768

1769+
/* the header must be checked already in ext4_ext_remove_space() */
17411770
ext_debug("truncate since %lu in leaf\n", start);
17421771
if (!path[depth].p_hdr)
17431772
path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
17441773
eh = path[depth].p_hdr;
17451774
BUG_ON(eh == NULL);
1746-
BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max));
1747-
BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC);
17481775

17491776
/* find where to start removing */
17501777
ex = EXT_LAST_EXTENT(eh);
@@ -1898,7 +1925,7 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start)
18981925
return -ENOMEM;
18991926
}
19001927
path[0].p_hdr = ext_inode_hdr(inode);
1901-
if (ext4_ext_check_header(__FUNCTION__, inode, path[0].p_hdr)) {
1928+
if (ext4_ext_check_header(inode, path[0].p_hdr, depth)) {
19021929
err = -EIO;
19031930
goto out;
19041931
}
@@ -1919,17 +1946,8 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start)
19191946
if (!path[i].p_hdr) {
19201947
ext_debug("initialize header\n");
19211948
path[i].p_hdr = ext_block_hdr(path[i].p_bh);
1922-
if (ext4_ext_check_header(__FUNCTION__, inode,
1923-
path[i].p_hdr)) {
1924-
err = -EIO;
1925-
goto out;
1926-
}
19271949
}
19281950

1929-
BUG_ON(le16_to_cpu(path[i].p_hdr->eh_entries)
1930-
> le16_to_cpu(path[i].p_hdr->eh_max));
1931-
BUG_ON(path[i].p_hdr->eh_magic != EXT4_EXT_MAGIC);
1932-
19331951
if (!path[i].p_idx) {
19341952
/* this level hasn't been touched yet */
19351953
path[i].p_idx = EXT_LAST_INDEX(path[i].p_hdr);
@@ -1946,17 +1964,27 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start)
19461964
i, EXT_FIRST_INDEX(path[i].p_hdr),
19471965
path[i].p_idx);
19481966
if (ext4_ext_more_to_rm(path + i)) {
1967+
struct buffer_head *bh;
19491968
/* go to the next level */
19501969
ext_debug("move to level %d (block %llu)\n",
19511970
i + 1, idx_pblock(path[i].p_idx));
19521971
memset(path + i + 1, 0, sizeof(*path));
1953-
path[i+1].p_bh =
1954-
sb_bread(sb, idx_pblock(path[i].p_idx));
1955-
if (!path[i+1].p_bh) {
1972+
bh = sb_bread(sb, idx_pblock(path[i].p_idx));
1973+
if (!bh) {
19561974
/* should we reset i_size? */
19571975
err = -EIO;
19581976
break;
19591977
}
1978+
if (WARN_ON(i + 1 > depth)) {
1979+
err = -EIO;
1980+
break;
1981+
}
1982+
if (ext4_ext_check_header(inode, ext_block_hdr(bh),
1983+
depth - i - 1)) {
1984+
err = -EIO;
1985+
break;
1986+
}
1987+
path[i + 1].p_bh = bh;
19601988

19611989
/* save actual number of indexes since this
19621990
* number is changed at the next iteration */

0 commit comments

Comments
 (0)