aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2014-08-06 19:43:56 +0200
committerBen Hutchings <ben@decadent.org.uk>2014-09-13 23:41:44 +0100
commite1c88681a9d8b010813947745778cd557f83f0ab (patch)
tree1eeaf98b157f0fdd9f788534459e5714536231e6 /fs
parent67fddd870e5dfc50d104e721ceee13b01db93069 (diff)
downloadkernel_samsung_smdk4412-e1c88681a9d8b010813947745778cd557f83f0ab.zip
kernel_samsung_smdk4412-e1c88681a9d8b010813947745778cd557f83f0ab.tar.gz
kernel_samsung_smdk4412-e1c88681a9d8b010813947745778cd557f83f0ab.tar.bz2
reiserfs: Fix use after free in journal teardown
commit 01777836c87081e4f68c4a43c9abe6114805f91e upstream. If do_journal_release() races with do_journal_end() which requeues delayed works for transaction flushing, we can leave work items for flushing outstanding transactions queued while freeing them. That results in use after free and possible crash in run_timers_softirq(). Fix the problem by not requeueing works if superblock is being shut down (MS_ACTIVE not set) and using cancel_delayed_work_sync() in do_journal_release(). Signed-off-by: Jan Kara <jack@suse.cz> [bwh: Backported to 3.2: - Adjust context - commit_wq is global, not per-superblock - Change comment about 'these works'; we only have one work item - Drop inapplicable changes to reiserfs_schedule_old_flush()] Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Diffstat (limited to 'fs')
-rw-r--r--fs/reiserfs/journal.c21
1 files changed, 16 insertions, 5 deletions
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
index eb71106..938a77f 100644
--- a/fs/reiserfs/journal.c
+++ b/fs/reiserfs/journal.c
@@ -1916,15 +1916,19 @@ static int do_journal_release(struct reiserfs_transaction_handle *th,
}
reiserfs_mounted_fs_count--;
- /* wait for all commits to finish */
- cancel_delayed_work(&SB_JOURNAL(sb)->j_work);
/*
* We must release the write lock here because
* the workqueue job (flush_async_commit) needs this lock
*/
reiserfs_write_unlock(sb);
- flush_workqueue(commit_wq);
+ /*
+ * Cancel flushing of old commits. Note that this work will not
+ * be requeued because superblock is being shutdown and doesn't
+ * have MS_ACTIVE set.
+ */
+ /* wait for all commits to finish */
+ cancel_delayed_work_sync(&SB_JOURNAL(sb)->j_work);
if (!reiserfs_mounted_fs_count) {
destroy_workqueue(commit_wq);
@@ -4211,8 +4215,15 @@ static int do_journal_end(struct reiserfs_transaction_handle *th,
if (flush) {
flush_commit_list(sb, jl, 1);
flush_journal_list(sb, jl, 1);
- } else if (!(jl->j_state & LIST_COMMIT_PENDING))
- queue_delayed_work(commit_wq, &journal->j_work, HZ / 10);
+ } else if (!(jl->j_state & LIST_COMMIT_PENDING)) {
+ /*
+ * Avoid queueing work when sb is being shut down. Transaction
+ * will be flushed on journal shutdown.
+ */
+ if (sb->s_flags & MS_ACTIVE)
+ queue_delayed_work(commit_wq,
+ &journal->j_work, HZ / 10);
+ }
/* if the next transaction has any chance of wrapping, flush
** transactions that might get overwritten. If any journal lists are very