Index: linux-2.6.16.54-0.2.5/fs/ext3/extents.c =================================================================== --- linux-2.6.16.54-0.2.5.orig/fs/ext3/extents.c +++ linux-2.6.16.54-0.2.5/fs/ext3/extents.c @@ -44,26 +44,60 @@ #include -static inline int ext3_ext_check_header(struct ext3_extent_header *eh) -{ - if (eh->eh_magic != EXT3_EXT_MAGIC) { - printk(KERN_ERR "EXT3-fs: invalid magic = 0x%x\n", - (unsigned)eh->eh_magic); - return -EIO; +static int __ext3_ext_check_header(const char *function, int line, struct inode *inode, + struct ext3_extent_header *eh, int depth, + int max) +{ + const char *error_msg = NULL; + + if (unlikely(eh->eh_magic != EXT3_EXT_MAGIC)) { + error_msg = "invalid magic"; + goto corrupted; + } + if (unlikely(eh->eh_depth != depth)) { + error_msg = "unexpected eh_depth"; + goto corrupted; + } + if (unlikely(eh->eh_max == 0)) { + error_msg = "too small eh_max"; + goto corrupted; } - if (eh->eh_max == 0) { - printk(KERN_ERR "EXT3-fs: invalid eh_max = %u\n", - (unsigned)eh->eh_max); - return -EIO; +#ifdef AGRESSIVE_TEST + if (eh->eh_max > 3) { + /* inode probably got its extent without defining + * AGRESSIVE_TEST */ + max = eh->eh_max; } - if (eh->eh_entries > eh->eh_max) { - printk(KERN_ERR "EXT3-fs: invalid eh_entries = %u\n", - (unsigned)eh->eh_entries); - return -EIO; +#endif + if (unlikely(eh->eh_max > max)) { + error_msg = "too large eh_max"; + goto corrupted; + } + if (unlikely(eh->eh_entries > eh->eh_max)) { + error_msg = "invalid eh_entries"; + goto corrupted; + } + if (unlikely((eh->eh_entries == 0) && (eh->eh_depth != 0))) { + error_msg = "invalid index, eh_entries=0 && eh_depth != 0"; + goto corrupted; } return 0; + +corrupted: + ext3_error(inode->i_sb, function, + ":%d: bad header in inode #%lu: %s - magic %x, " + "entries %u, max %u(%u), depth %u(%u)", line, + inode->i_ino, error_msg, eh->eh_magic, + eh->eh_entries, eh->eh_max, max, + eh->eh_depth, depth); + + return -EIO; } +#define ext3_ext_check_header(inode,eh,depth,max) \ + __ext3_ext_check_header(__FUNCTION__,__LINE__,inode,eh,depth,max) + + static handle_t *ext3_ext_journal_restart(handle_t *handle, int needed) { int err; @@ -226,6 +260,26 @@ static inline int ext3_ext_space_root_id return size; } +static inline int +ext3_ext_max_entries(struct ext3_extents_tree *tree, int root, int depth) +{ + int max; + + if (root) { + if (depth == 0) + max = ext3_ext_space_root(tree); + else + max = ext3_ext_space_root_idx(tree); + } else { + if (depth == 0) + max = ext3_ext_space_block(tree); + else + max = ext3_ext_space_block_idx(tree); + } + + return max; +} + static void ext3_ext_show_path(struct ext3_extents_tree *tree, struct ext3_ext_path *path) { @@ -296,10 +350,6 @@ ext3_ext_binsearch_idx(struct ext3_exten struct ext3_extent_idx *ix; int l = 0, k, r; - EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC); - EXT_ASSERT(eh->eh_entries <= eh->eh_max); - EXT_ASSERT(eh->eh_entries > 0); - ext_debug(tree, "binsearch for %d(idx): ", block); path->p_idx = ix = EXT_FIRST_INDEX(eh); @@ -359,9 +409,6 @@ ext3_ext_binsearch(struct ext3_extents_t struct ext3_extent *ex; int l = 0, k, r; - EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC); - EXT_ASSERT(eh->eh_entries <= eh->eh_max); - if (eh->eh_entries == 0) { /* * this leaf is empty yet: @@ -436,6 +483,7 @@ ext3_ext_find_extent(struct ext3_extents struct ext3_extent_header *eh; struct buffer_head *bh; int depth, i, ppos = 0; + int max; EXT_ASSERT(tree); EXT_ASSERT(tree->inode); @@ -443,17 +491,15 @@ ext3_ext_find_extent(struct ext3_extents eh = EXT_ROOT_HDR(tree); EXT_ASSERT(eh); - if (ext3_ext_check_header(eh)) { + i = depth = EXT_DEPTH(tree); + max = ext3_ext_max_entries(tree, 1, i); + if (ext3_ext_check_header(tree->inode, eh, i, max)) { /* don't free previously allocated path * -- caller should take care */ path = NULL; goto err; } - i = depth = EXT_DEPTH(tree); - EXT_ASSERT(eh->eh_max); - EXT_ASSERT(eh->eh_magic == EXT3_EXT_MAGIC); - /* account possible depth increase */ if (!path) { path = kmalloc(sizeof(struct ext3_ext_path) * (depth + 2), @@ -484,7 +530,8 @@ ext3_ext_find_extent(struct ext3_extents path[ppos].p_hdr = eh; i--; - if (ext3_ext_check_header(eh)) + max = ext3_ext_max_entries(tree, 0, i); + if (ext3_ext_check_header(tree->inode, eh, i, max)) goto err; } @@ -493,9 +540,6 @@ ext3_ext_find_extent(struct ext3_extents path[ppos].p_ext = NULL; path[ppos].p_idx = NULL; - if (ext3_ext_check_header(eh)) - goto err; - /* find extent */ ext3_ext_binsearch(tree, path + ppos, block); @@ -992,7 +1036,7 @@ ext3_ext_search_right(struct ext3_extent struct ext3_extent_idx *ix; struct ext3_extent *ex; unsigned long block; - int depth; + int depth, max; BUG_ON(path == NULL); depth = path->p_depth; @@ -1050,7 +1094,9 @@ ext3_ext_search_right(struct ext3_extent if (bh == NULL) return -EIO; eh = EXT_BLOCK_HDR(bh); - if (ext3_ext_check_header(eh)) { + max = ext3_ext_max_entries(tree, 0, depth); + if (ext3_ext_check_header(tree->inode, eh, + path->p_depth - depth, max)) { brelse(bh); return -EIO; } @@ -1063,7 +1109,8 @@ ext3_ext_search_right(struct ext3_extent if (bh == NULL) return -EIO; eh = EXT_BLOCK_HDR(bh); - if (ext3_ext_check_header(eh)) { + max = ext3_ext_max_entries(tree, 0, depth); + if (ext3_ext_check_header(tree->inode, eh, 0, max)) { brelse(bh); return -EIO; } @@ -1693,6 +1740,8 @@ ext3_ext_rm_leaf(handle_t *handle, struc ext_debug(tree, "remove [%lu:%lu] in leaf\n", start, end); if (!path[depth].p_hdr) path[depth].p_hdr = EXT_BLOCK_HDR(path[depth].p_bh); + + /* the header must be checked already in ext3_ext_remove_space() */ eh = path[depth].p_hdr; EXT_ASSERT(eh); EXT_ASSERT(eh->eh_entries <= eh->eh_max); @@ -1855,7 +1904,7 @@ int ext3_ext_remove_space(struct ext3_ex int depth = EXT_DEPTH(tree); struct ext3_ext_path *path; handle_t *handle; - int i = 0, err = 0; + int i = 0, err = 0, max; ext_debug(tree, "space to be removed: %lu:%lu\n", start, end); @@ -1878,7 +1927,13 @@ int ext3_ext_remove_space(struct ext3_ex } memset(path, 0, sizeof(struct ext3_ext_path) * (depth + 1)); path[i].p_hdr = EXT_ROOT_HDR(tree); - + + max = ext3_ext_max_entries(tree, 1, depth); + if (ext3_ext_check_header(inode, path[i].p_hdr, depth, max)) { + err = -EIO; + goto out; + } + while (i >= 0 && err == 0) { if (i == depth) { /* this is leaf block */ @@ -1888,16 +1943,13 @@ int ext3_ext_remove_space(struct ext3_ex i--; continue; } - + /* this is index block */ if (!path[i].p_hdr) { ext_debug(tree, "initialize header\n"); path[i].p_hdr = EXT_BLOCK_HDR(path[i].p_bh); } - EXT_ASSERT(path[i].p_hdr->eh_entries <= path[i].p_hdr->eh_max); - EXT_ASSERT(path[i].p_hdr->eh_magic == EXT3_EXT_MAGIC); - if (!path[i].p_idx) { /* this level hasn't touched yet */ path[i].p_idx = @@ -1924,6 +1976,14 @@ int ext3_ext_remove_space(struct ext3_ex err = -EIO; break; } + BUG_ON(i + 1 > depth); + max = ext3_ext_max_entries(tree, 0, depth - i - 1); + if (ext3_ext_check_header(inode, + EXT_BLOCK_HDR(path[i+1].p_bh), + depth - i - 1, max)) { + err = -EIO; + break; + } /* put actual number of indexes to know is this * number got changed at the next iteration */ path[i].p_block = path[i].p_hdr->eh_entries; @@ -1944,7 +2004,7 @@ int ext3_ext_remove_space(struct ext3_ex } /* TODO: flexible tree reduction should be here */ - if (path->p_hdr->eh_entries == 0) { + if (err == 0 && path->p_hdr->eh_entries == 0) { /* * truncate to zero freed all the tree * so, we need to correct eh_depth @@ -1958,6 +2018,7 @@ int ext3_ext_remove_space(struct ext3_ex } ext3_ext_tree_changed(tree); +out: kfree(path); ext3_journal_stop(handle);