87 lines
3.0 KiB
Diff
87 lines
3.0 KiB
Diff
From: Jan Kara <jack@suse.cz>
|
|
Date: Wed, 6 Aug 2014 19:43:56 +0200
|
|
Subject: [2/2] reiserfs: Fix use after free in journal teardown
|
|
Origin: https://git.kernel.org/linus/01777836c87081e4f68c4a43c9abe6114805f91e
|
|
|
|
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().
|
|
|
|
CC: stable@vger.kernel.org
|
|
Signed-off-by: Jan Kara <jack@suse.cz>
|
|
---
|
|
fs/reiserfs/journal.c | 22 ++++++++++++++++------
|
|
fs/reiserfs/super.c | 6 +++++-
|
|
2 files changed, 21 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
|
|
index e8870de..a88b1b3 100644
|
|
--- a/fs/reiserfs/journal.c
|
|
+++ b/fs/reiserfs/journal.c
|
|
@@ -1947,8 +1947,6 @@ static int do_journal_release(struct reiserfs_transaction_handle *th,
|
|
}
|
|
}
|
|
|
|
- /* wait for all commits to finish */
|
|
- cancel_delayed_work(&SB_JOURNAL(sb)->j_work);
|
|
|
|
/*
|
|
* We must release the write lock here because
|
|
@@ -1956,8 +1954,14 @@ static int do_journal_release(struct reiserfs_transaction_handle *th,
|
|
*/
|
|
reiserfs_write_unlock(sb);
|
|
|
|
+ /*
|
|
+ * Cancel flushing of old commits. Note that neither of these works
|
|
+ * will be requeued because superblock is being shutdown and doesn't
|
|
+ * have MS_ACTIVE set.
|
|
+ */
|
|
cancel_delayed_work_sync(&REISERFS_SB(sb)->old_work);
|
|
- flush_workqueue(REISERFS_SB(sb)->commit_wq);
|
|
+ /* wait for all commits to finish */
|
|
+ cancel_delayed_work_sync(&SB_JOURNAL(sb)->j_work);
|
|
|
|
free_journal_ram(sb);
|
|
|
|
@@ -4292,9 +4296,15 @@ static int do_journal_end(struct reiserfs_transaction_handle *th, int flags)
|
|
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(REISERFS_SB(sb)->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(REISERFS_SB(sb)->commit_wq,
|
|
+ &journal->j_work, HZ / 10);
|
|
+ }
|
|
|
|
/*
|
|
* if the next transaction has any chance of wrapping, flush
|
|
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
|
|
index a392cef..5fd8f57 100644
|
|
--- a/fs/reiserfs/super.c
|
|
+++ b/fs/reiserfs/super.c
|
|
@@ -100,7 +100,11 @@ void reiserfs_schedule_old_flush(struct super_block *s)
|
|
struct reiserfs_sb_info *sbi = REISERFS_SB(s);
|
|
unsigned long delay;
|
|
|
|
- if (s->s_flags & MS_RDONLY)
|
|
+ /*
|
|
+ * Avoid scheduling flush when sb is being shut down. It can race
|
|
+ * with journal shutdown and free still queued delayed work.
|
|
+ */
|
|
+ if (s->s_flags & MS_RDONLY || !(s->s_flags & MS_ACTIVE))
|
|
return;
|
|
|
|
spin_lock(&sbi->old_work_lock);
|