Skip to content

Commit

Permalink
ext3: ext3_bread usage audit
Browse files Browse the repository at this point in the history
This is the ext3 version of the same patch applied to Ext4, where such goal is
to audit the usage of ext3_bread() due a possible misinterpretion of its return
value.

Focused on directory blocks, a NULL value returned from ext3_bread() means a
hole, which cannot exist into a directory inode. It can pass undetected after a
fix in an uninitialized error variable.

The (now) initialized variable into ext3_getblk() may lead to a zero'ed return
value of ext3_bread() to its callers, which can make the caller do not detect
the hole in the directory inode.

This patch creates a new wrapper function ext3_dir_bread() which checks for
holes properly, reports error, and returns EIO in that case.

Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
Signed-off-by: Jan Kara <jack@suse.cz>
  • Loading branch information
Carlos Maiolino authored and Jan Kara committed Oct 9, 2012
1 parent aa96601 commit c3d59ad
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 18 deletions.
38 changes: 20 additions & 18 deletions fs/ext3/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ static struct buffer_head *ext3_append(handle_t *handle,

*block = inode->i_size >> inode->i_sb->s_blocksize_bits;

bh = ext3_bread(handle, inode, *block, 1, err);
if (bh) {
if ((bh = ext3_dir_bread(handle, inode, *block, 1, err))) {
inode->i_size += inode->i_sb->s_blocksize;
EXT3_I(inode)->i_disksize = inode->i_size;
*err = ext3_journal_get_write_access(handle, bh);
Expand Down Expand Up @@ -339,8 +338,10 @@ dx_probe(struct qstr *entry, struct inode *dir,
u32 hash;

frame->bh = NULL;
if (!(bh = ext3_bread (NULL,dir, 0, 0, err)))
if (!(bh = ext3_dir_bread(NULL, dir, 0, 0, err))) {
*err = ERR_BAD_DX_DIR;
goto fail;
}
root = (struct dx_root *) bh->b_data;
if (root->info.hash_version != DX_HASH_TEA &&
root->info.hash_version != DX_HASH_HALF_MD4 &&
Expand Down Expand Up @@ -436,8 +437,10 @@ dx_probe(struct qstr *entry, struct inode *dir,
frame->entries = entries;
frame->at = at;
if (!indirect--) return frame;
if (!(bh = ext3_bread (NULL,dir, dx_get_block(at), 0, err)))
if (!(bh = ext3_dir_bread(NULL, dir, dx_get_block(at), 0, err))) {
*err = ERR_BAD_DX_DIR;
goto fail2;
}
at = entries = ((struct dx_node *) bh->b_data)->entries;
if (dx_get_limit(entries) != dx_node_limit (dir)) {
ext3_warning(dir->i_sb, __func__,
Expand Down Expand Up @@ -535,8 +538,8 @@ static int ext3_htree_next_block(struct inode *dir, __u32 hash,
* block so no check is necessary
*/
while (num_frames--) {
if (!(bh = ext3_bread(NULL, dir, dx_get_block(p->at),
0, &err)))
if (!(bh = ext3_dir_bread(NULL, dir, dx_get_block(p->at),
0, &err)))
return err; /* Failure */
p++;
brelse (p->bh);
Expand All @@ -562,7 +565,8 @@ static int htree_dirblock_to_tree(struct file *dir_file,
int err = 0, count = 0;

dxtrace(printk("In htree dirblock_to_tree: block %d\n", block));
if (!(bh = ext3_bread (NULL, dir, block, 0, &err)))

if (!(bh = ext3_dir_bread(NULL, dir, block, 0, &err)))
return err;

de = (struct ext3_dir_entry_2 *) bh->b_data;
Expand Down Expand Up @@ -976,7 +980,7 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir,
return NULL;
do {
block = dx_get_block(frame->at);
if (!(bh = ext3_bread (NULL,dir, block, 0, err)))
if (!(bh = ext3_dir_bread (NULL, dir, block, 0, err)))
goto errout;

retval = search_dirblock(bh, dir, entry,
Expand Down Expand Up @@ -1458,9 +1462,9 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
}
blocks = dir->i_size >> sb->s_blocksize_bits;
for (block = 0; block < blocks; block++) {
bh = ext3_bread(handle, dir, block, 0, &retval);
if(!bh)
if (!(bh = ext3_dir_bread(handle, dir, block, 0, &retval)))
return retval;

retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
if (retval != -ENOSPC)
return retval;
Expand Down Expand Up @@ -1500,7 +1504,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,
entries = frame->entries;
at = frame->at;

if (!(bh = ext3_bread(handle,dir, dx_get_block(frame->at), 0, &err)))
if (!(bh = ext3_dir_bread(handle, dir, dx_get_block(frame->at), 0, &err)))
goto cleanup;

BUFFER_TRACE(bh, "get_write_access");
Expand Down Expand Up @@ -1790,8 +1794,7 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
inode->i_op = &ext3_dir_inode_operations;
inode->i_fop = &ext3_dir_operations;
inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize;
dir_block = ext3_bread (handle, inode, 0, 1, &err);
if (!dir_block)
if (!(dir_block = ext3_dir_bread(handle, inode, 0, 1, &err)))
goto out_clear_inode;

BUFFER_TRACE(dir_block, "get_write_access");
Expand Down Expand Up @@ -1859,7 +1862,7 @@ static int empty_dir (struct inode * inode)

sb = inode->i_sb;
if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) ||
!(bh = ext3_bread (NULL, inode, 0, 0, &err))) {
!(bh = ext3_dir_bread(NULL, inode, 0, 0, &err))) {
if (err)
ext3_error(inode->i_sb, __func__,
"error %d reading directory #%lu offset 0",
Expand Down Expand Up @@ -1890,9 +1893,8 @@ static int empty_dir (struct inode * inode)
(void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
err = 0;
brelse (bh);
bh = ext3_bread (NULL, inode,
offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err);
if (!bh) {
if (!(bh = ext3_dir_bread (NULL, inode,
offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err))) {
if (err)
ext3_error(sb, __func__,
"error %d reading directory"
Expand Down Expand Up @@ -2388,7 +2390,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
goto end_rename;
}
retval = -EIO;
dir_bh = ext3_bread (handle, old_inode, 0, 0, &retval);
dir_bh = ext3_dir_bread(handle, old_inode, 0, 0, &retval);
if (!dir_bh)
goto end_rename;
if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
Expand Down
19 changes: 19 additions & 0 deletions fs/ext3/namei.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,22 @@
*/

extern struct dentry *ext3_get_parent(struct dentry *child);

static inline struct buffer_head *ext3_dir_bread(handle_t *handle,
struct inode *inode,
int block, int create,
int *err)
{
struct buffer_head *bh;

bh = ext3_bread(handle, inode, block, create, err);

if (!bh && !(*err)) {
*err = -EIO;
ext3_error(inode->i_sb, __func__,
"Directory hole detected on inode %lu\n",
inode->i_ino);
return NULL;
}
return bh;
}

0 comments on commit c3d59ad

Please sign in to comment.