Commit 7dbfb92c authored by Andrew Morton's avatar Andrew Morton Committed by David S. Miller

[PATCH] ext3: fix use-after-free bug

ext3_writepage() calls ext3_journal_stop(), which dereferences the affected
inode.

It does this _after_ writing the page out, which is illegal.  The IO can
complete, the page can be repeased from the inode and the inode can be freed
up.

It's a long-standing bug.  It has been reported happening on preemptible
kernels, where the timing window is larger.

Fix that up by teaching ext3_journal_stop to locate the superblock via the
journal structure, not via the inode.

This means that ext3_journal_stop() does not need the inode argument at all.

Also uninline the affected functions.  It saves 5.5 kbytes.

Also remove the setting of sb->s_dirt in ext3_journal_stop().  That was an
awkward way of telling sys_sync() that the filesystem needs a commit, and
with the ext3_sync_fs() that is no longer needed.
parent 34f2047d
...@@ -420,7 +420,7 @@ ext3_acl_chmod(struct inode *inode) ...@@ -420,7 +420,7 @@ ext3_acl_chmod(struct inode *inode)
return PTR_ERR(handle); return PTR_ERR(handle);
} }
error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone); error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, clone);
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
} }
posix_acl_release(clone); posix_acl_release(clone);
return error; return error;
...@@ -522,7 +522,7 @@ ext3_xattr_set_acl(struct inode *inode, int type, const void *value, ...@@ -522,7 +522,7 @@ ext3_xattr_set_acl(struct inode *inode, int type, const void *value,
if (IS_ERR(handle)) if (IS_ERR(handle))
return PTR_ERR(handle); return PTR_ERR(handle);
error = ext3_set_acl(handle, inode, type, acl); error = ext3_set_acl(handle, inode, type, acl);
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
release_and_out: release_and_out:
posix_acl_release(acl); posix_acl_release(acl);
......
...@@ -235,7 +235,7 @@ void ext3_delete_inode (struct inode * inode) ...@@ -235,7 +235,7 @@ void ext3_delete_inode (struct inode * inode)
clear_inode(inode); clear_inode(inode);
else else
ext3_free_inode(handle, inode); ext3_free_inode(handle, inode);
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return; return;
no_delete: no_delete:
...@@ -1107,7 +1107,7 @@ static int ext3_prepare_write(struct file *file, struct page *page, ...@@ -1107,7 +1107,7 @@ static int ext3_prepare_write(struct file *file, struct page *page,
} }
prepare_write_failed: prepare_write_failed:
if (ret) if (ret)
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
out: out:
unlock_kernel(); unlock_kernel();
return ret; return ret;
...@@ -1176,7 +1176,7 @@ static int ext3_commit_write(struct file *file, struct page *page, ...@@ -1176,7 +1176,7 @@ static int ext3_commit_write(struct file *file, struct page *page,
if (!ret) if (!ret)
ret = ret2; ret = ret2;
} }
ret2 = ext3_journal_stop(handle, inode); ret2 = ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
if (!ret) if (!ret)
ret = ret2; ret = ret2;
...@@ -1374,7 +1374,7 @@ static int ext3_writepage(struct page *page, struct writeback_control *wbc) ...@@ -1374,7 +1374,7 @@ static int ext3_writepage(struct page *page, struct writeback_control *wbc)
PAGE_CACHE_SIZE, NULL, bput_one); PAGE_CACHE_SIZE, NULL, bput_one);
} }
err = ext3_journal_stop(handle, inode); err = ext3_journal_stop(handle);
if (!ret) if (!ret)
ret = err; ret = err;
unlock_kernel(); unlock_kernel();
...@@ -1479,7 +1479,7 @@ static int ext3_direct_IO(int rw, struct kiocb *iocb, ...@@ -1479,7 +1479,7 @@ static int ext3_direct_IO(int rw, struct kiocb *iocb,
ret = err; ret = err;
} }
} }
err = ext3_journal_stop(handle, inode); err = ext3_journal_stop(handle);
if (ret == 0) if (ret == 0)
ret = err; ret = err;
unlock_kernel(); unlock_kernel();
...@@ -2140,7 +2140,7 @@ void ext3_truncate(struct inode * inode) ...@@ -2140,7 +2140,7 @@ void ext3_truncate(struct inode * inode)
if (inode->i_nlink) if (inode->i_nlink)
ext3_orphan_del(handle, inode); ext3_orphan_del(handle, inode);
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
} }
...@@ -2560,7 +2560,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -2560,7 +2560,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
rc = ext3_mark_inode_dirty(handle, inode); rc = ext3_mark_inode_dirty(handle, inode);
if (!error) if (!error)
error = rc; error = rc;
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
} }
rc = inode_setattr(inode, attr); rc = inode_setattr(inode, attr);
...@@ -2737,7 +2737,7 @@ void ext3_dirty_inode(struct inode *inode) ...@@ -2737,7 +2737,7 @@ void ext3_dirty_inode(struct inode *inode)
current_handle); current_handle);
ext3_mark_inode_dirty(handle, inode); ext3_mark_inode_dirty(handle, inode);
} }
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
out: out:
unlock_kernel(); unlock_kernel();
} }
...@@ -2818,7 +2818,7 @@ int ext3_change_inode_journal_flag(struct inode *inode, int val) ...@@ -2818,7 +2818,7 @@ int ext3_change_inode_journal_flag(struct inode *inode, int val)
err = ext3_mark_inode_dirty(handle, inode); err = ext3_mark_inode_dirty(handle, inode);
handle->h_sync = 1; handle->h_sync = 1;
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
ext3_std_error(inode->i_sb, err); ext3_std_error(inode->i_sb, err);
return err; return err;
......
...@@ -90,7 +90,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ...@@ -90,7 +90,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
err = ext3_mark_iloc_dirty(handle, inode, &iloc); err = ext3_mark_iloc_dirty(handle, inode, &iloc);
flags_err: flags_err:
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
if (err) if (err)
return err; return err;
...@@ -126,7 +126,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ...@@ -126,7 +126,7 @@ int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
inode->i_generation = generation; inode->i_generation = generation;
err = ext3_mark_iloc_dirty(handle, inode, &iloc); err = ext3_mark_iloc_dirty(handle, inode, &iloc);
ext3_journal_stop(handle, inode); ext3_journal_stop(handle);
return err; return err;
} }
#ifdef CONFIG_JBD_DEBUG #ifdef CONFIG_JBD_DEBUG
......
...@@ -1615,7 +1615,7 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode) ...@@ -1615,7 +1615,7 @@ static int ext3_create (struct inode * dir, struct dentry * dentry, int mode)
inode->i_mapping->a_ops = &ext3_aops; inode->i_mapping->a_ops = &ext3_aops;
err = ext3_add_nondir(handle, dentry, inode); err = ext3_add_nondir(handle, dentry, inode);
} }
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return err; return err;
} }
...@@ -1647,7 +1647,7 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry, ...@@ -1647,7 +1647,7 @@ static int ext3_mknod (struct inode * dir, struct dentry *dentry,
#endif #endif
err = ext3_add_nondir(handle, dentry, inode); err = ext3_add_nondir(handle, dentry, inode);
} }
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return err; return err;
} }
...@@ -1721,7 +1721,7 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode) ...@@ -1721,7 +1721,7 @@ static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
ext3_mark_inode_dirty(handle, dir); ext3_mark_inode_dirty(handle, dir);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
out_stop: out_stop:
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return err; return err;
} }
...@@ -1994,7 +1994,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry) ...@@ -1994,7 +1994,7 @@ static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
ext3_mark_inode_dirty(handle, dir); ext3_mark_inode_dirty(handle, dir);
end_rmdir: end_rmdir:
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
brelse (bh); brelse (bh);
unlock_kernel(); unlock_kernel();
return retval; return retval;
...@@ -2050,7 +2050,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry) ...@@ -2050,7 +2050,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)
retval = 0; retval = 0;
end_unlink: end_unlink:
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
brelse (bh); brelse (bh);
return retval; return retval;
...@@ -2109,7 +2109,7 @@ static int ext3_symlink (struct inode * dir, ...@@ -2109,7 +2109,7 @@ static int ext3_symlink (struct inode * dir,
EXT3_I(inode)->i_disksize = inode->i_size; EXT3_I(inode)->i_disksize = inode->i_size;
err = ext3_add_nondir(handle, dentry, inode); err = ext3_add_nondir(handle, dentry, inode);
out_stop: out_stop:
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return err; return err;
} }
...@@ -2142,7 +2142,7 @@ static int ext3_link (struct dentry * old_dentry, ...@@ -2142,7 +2142,7 @@ static int ext3_link (struct dentry * old_dentry,
atomic_inc(&inode->i_count); atomic_inc(&inode->i_count);
err = ext3_add_nondir(handle, dentry, inode); err = ext3_add_nondir(handle, dentry, inode);
ext3_journal_stop(handle, dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return err; return err;
} }
...@@ -2299,7 +2299,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry, ...@@ -2299,7 +2299,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
brelse (dir_bh); brelse (dir_bh);
brelse (old_bh); brelse (old_bh);
brelse (new_bh); brelse (new_bh);
ext3_journal_stop(handle, old_dir); ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return retval; return retval;
} }
......
...@@ -108,6 +108,72 @@ static void clear_ro_after(struct super_block *sb) ...@@ -108,6 +108,72 @@ static void clear_ro_after(struct super_block *sb)
#define clear_ro_after(sb) do {} while (0) #define clear_ro_after(sb) do {} while (0)
#endif #endif
/*
* Wrappers for journal_start/end.
*
* The only special thing we need to do here is to make sure that all
* journal_end calls result in the superblock being marked dirty, so
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
handle_t *ext3_journal_start(struct inode *inode, int nblocks)
{
journal_t *journal;
if (inode->i_sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS);
/* Special case here: if the journal has aborted behind our
* backs (eg. EIO in the commit thread), then we still need to
* take the FS itself readonly cleanly. */
journal = EXT3_JOURNAL(inode);
if (is_journal_aborted(journal)) {
ext3_abort(inode->i_sb, __FUNCTION__,
"Detected aborted journal");
return ERR_PTR(-EROFS);
}
return journal_start(journal, nblocks);
}
/*
* The only special thing we need to do here is to make sure that all
* journal_stop calls result in the superblock being marked dirty, so
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
int __ext3_journal_stop(const char *where, handle_t *handle)
{
struct super_block *sb;
int err;
int rc;
sb = handle->h_transaction->t_journal->j_private;
err = handle->h_err;
rc = journal_stop(handle);
if (!err)
err = rc;
if (err)
__ext3_std_error(sb, where, err);
return err;
}
void ext3_journal_abort_handle(const char *caller, const char *err_fn,
struct buffer_head *bh, handle_t *handle, int err)
{
char nbuf[16];
const char *errstr = ext3_decode_error(NULL, err, nbuf);
printk(KERN_ERR "%s: aborting transaction: %s in %s",
caller, errstr, err_fn);
if (bh)
BUFFER_TRACE(bh, "abort");
journal_abort_handle(handle);
if (!handle->h_err)
handle->h_err = err;
}
static char error_buf[1024]; static char error_buf[1024];
...@@ -1431,6 +1497,7 @@ static journal_t *ext3_get_journal(struct super_block *sb, int journal_inum) ...@@ -1431,6 +1497,7 @@ static journal_t *ext3_get_journal(struct super_block *sb, int journal_inum)
printk(KERN_ERR "EXT3-fs: Could not load journal inode\n"); printk(KERN_ERR "EXT3-fs: Could not load journal inode\n");
iput(journal_inode); iput(journal_inode);
} }
journal->j_private = sb;
ext3_init_journal_params(EXT3_SB(sb), journal); ext3_init_journal_params(EXT3_SB(sb), journal);
return journal; return journal;
} }
...@@ -1495,6 +1562,7 @@ static journal_t *ext3_get_dev_journal(struct super_block *sb, ...@@ -1495,6 +1562,7 @@ static journal_t *ext3_get_dev_journal(struct super_block *sb,
printk(KERN_ERR "EXT3-fs: failed to create device journal\n"); printk(KERN_ERR "EXT3-fs: failed to create device journal\n");
goto out_bdev; goto out_bdev;
} }
journal->j_private = sb;
ll_rw_block(READ, 1, &journal->j_sb_buffer); ll_rw_block(READ, 1, &journal->j_sb_buffer);
wait_on_buffer(journal->j_sb_buffer); wait_on_buffer(journal->j_sb_buffer);
if (!buffer_uptodate(journal->j_sb_buffer)) { if (!buffer_uptodate(journal->j_sb_buffer)) {
...@@ -1561,7 +1629,6 @@ static int ext3_load_journal(struct super_block * sb, ...@@ -1561,7 +1629,6 @@ static int ext3_load_journal(struct super_block * sb,
if (!(journal = ext3_get_dev_journal(sb, journal_dev))) if (!(journal = ext3_get_dev_journal(sb, journal_dev)))
return -EINVAL; return -EINVAL;
} }
if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) { if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) {
err = journal_update_format(journal); err = journal_update_format(journal);
......
...@@ -855,7 +855,7 @@ ext3_xattr_set(struct inode *inode, int name_index, const char *name, ...@@ -855,7 +855,7 @@ ext3_xattr_set(struct inode *inode, int name_index, const char *name,
else else
error = ext3_xattr_set_handle(handle, inode, name_index, name, error = ext3_xattr_set_handle(handle, inode, name_index, name,
value, value_len, flags); value, value_len, flags);
error2 = ext3_journal_stop(handle, inode); error2 = ext3_journal_stop(handle);
unlock_kernel(); unlock_kernel();
return error ? error : error2; return error ? error : error2;
......
...@@ -93,24 +93,8 @@ int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode); ...@@ -93,24 +93,8 @@ int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode);
* been done yet. * been done yet.
*/ */
static inline void ext3_journal_abort_handle(const char *caller, void ext3_journal_abort_handle(const char *caller, const char *err_fn,
const char *err_fn, struct buffer_head *bh, handle_t *handle, int err);
struct buffer_head *bh,
handle_t *handle,
int err)
{
char nbuf[16];
const char *errstr = ext3_decode_error(NULL, err, nbuf);
printk(KERN_ERR "%s: aborting transaction: %s in %s",
caller, errstr, err_fn);
if (bh)
BUFFER_TRACE(bh, "abort");
journal_abort_handle(handle);
if (!handle->h_err)
handle->h_err = err;
}
static inline int static inline int
__ext3_journal_get_undo_access(const char *where, __ext3_journal_get_undo_access(const char *where,
...@@ -180,57 +164,11 @@ __ext3_journal_dirty_metadata(const char *where, ...@@ -180,57 +164,11 @@ __ext3_journal_dirty_metadata(const char *where,
#define ext3_journal_dirty_metadata(handle, bh) \ #define ext3_journal_dirty_metadata(handle, bh) \
__ext3_journal_dirty_metadata(__FUNCTION__, (handle), (bh)) __ext3_journal_dirty_metadata(__FUNCTION__, (handle), (bh))
handle_t *ext3_journal_start(struct inode *inode, int nblocks);
int __ext3_journal_stop(const char *where, handle_t *handle);
#define ext3_journal_stop(handle) \
/* __ext3_journal_stop(__FUNCTION__, (handle))
* Wrappers for journal_start/end.
*
* The only special thing we need to do here is to make sure that all
* journal_end calls result in the superblock being marked dirty, so
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
static inline handle_t *ext3_journal_start(struct inode *inode, int nblocks)
{
journal_t *journal;
if (inode->i_sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS);
/* Special case here: if the journal has aborted behind our
* backs (eg. EIO in the commit thread), then we still need to
* take the FS itself readonly cleanly. */
journal = EXT3_JOURNAL(inode);
if (is_journal_aborted(journal)) {
ext3_abort(inode->i_sb, __FUNCTION__,
"Detected aborted journal");
return ERR_PTR(-EROFS);
}
return journal_start(journal, nblocks);
}
/*
* The only special thing we need to do here is to make sure that all
* journal_stop calls result in the superblock being marked dirty, so
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
static inline int __ext3_journal_stop(const char *where,
handle_t *handle, struct inode *inode)
{
int err = handle->h_err;
int rc = journal_stop(handle);
inode->i_sb->s_dirt = 1;
if (!err)
err = rc;
if (err)
__ext3_std_error(inode->i_sb, where, err);
return err;
}
#define ext3_journal_stop(handle, inode) \
__ext3_journal_stop(__FUNCTION__, (handle), (inode))
static inline handle_t *ext3_journal_current_handle(void) static inline handle_t *ext3_journal_current_handle(void)
{ {
......
...@@ -633,6 +633,10 @@ struct journal_s ...@@ -633,6 +633,10 @@ struct journal_s
/* The revoke table: maintains the list of revoked blocks in the */ /* The revoke table: maintains the list of revoked blocks in the */
/* current transaction. */ /* current transaction. */
struct jbd_revoke_table_s *j_revoke; struct jbd_revoke_table_s *j_revoke;
/* An opaque pointer to fs-private information. ext3 puts its
* superblock pointer here */
void *j_private;
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment