aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jbd2
diff options
context:
space:
mode:
authorLukas Czerner <lczerner@redhat.com>2015-05-14 18:55:18 -0400
committerTheodore Ts'o <tytso@mit.edu>2015-05-14 18:55:18 -0400
commit9d506594069355d1fb2de3f9104667312ff08ed3 (patch)
treee3e6bcf3c89f03a0b2bc68664fd04a6d39cbd0c6 /fs/jbd2
parent92c82639106214a9d34daa2bc791605eb1faab07 (diff)
ext4: fix NULL pointer dereference when journal restart fails
Currently when journal restart fails, we'll have the h_transaction of the handle set to NULL to indicate that the handle has been effectively aborted. We handle this situation quietly in the jbd2_journal_stop() and just free the handle and exit because everything else has been done before we attempted (and failed) to restart the journal. Unfortunately there are a number of problems with that approach introduced with commit 41a5b913197c "jbd2: invalidate handle if jbd2_journal_restart() fails" First of all in ext4 jbd2_journal_stop() will be called through __ext4_journal_stop() where we would try to get a hold of the superblock by dereferencing h_transaction which in this case would lead to NULL pointer dereference and crash. In addition we're going to free the handle regardless of the refcount which is bad as well, because others up the call chain will still reference the handle so we might potentially reference already freed memory. Moreover it's expected that we'll get aborted handle as well as detached handle in some of the journalling function as the error propagates up the stack, so it's unnecessary to call WARN_ON every time we get detached handle. And finally we might leak some memory by forgetting to free reserved handle in jbd2_journal_stop() in the case where handle was detached from the transaction (h_transaction is NULL). Fix the NULL pointer dereference in __ext4_journal_stop() by just calling jbd2_journal_stop() quietly as suggested by Jan Kara. Also fix the potential memory leak in jbd2_journal_stop() and use proper handle refcounting before we attempt to free it to avoid use-after-free issues. And finally remove all WARN_ON(!transaction) from the code so that we do not get random traces when something goes wrong because when journal restart fails we will get to some of those functions. Cc: stable@vger.kernel.org Signed-off-by: Lukas Czerner <lczerner@redhat.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Reviewed-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/jbd2')
-rw-r--r--fs/jbd2/transaction.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index 5f09370c90a8..ff2f2e6ad311 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -551,7 +551,6 @@ int jbd2_journal_extend(handle_t *handle, int nblocks)
551 int result; 551 int result;
552 int wanted; 552 int wanted;
553 553
554 WARN_ON(!transaction);
555 if (is_handle_aborted(handle)) 554 if (is_handle_aborted(handle))
556 return -EROFS; 555 return -EROFS;
557 journal = transaction->t_journal; 556 journal = transaction->t_journal;
@@ -627,7 +626,6 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask)
627 tid_t tid; 626 tid_t tid;
628 int need_to_start, ret; 627 int need_to_start, ret;
629 628
630 WARN_ON(!transaction);
631 /* If we've had an abort of any type, don't even think about 629 /* If we've had an abort of any type, don't even think about
632 * actually doing the restart! */ 630 * actually doing the restart! */
633 if (is_handle_aborted(handle)) 631 if (is_handle_aborted(handle))
@@ -785,7 +783,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
785 int need_copy = 0; 783 int need_copy = 0;
786 unsigned long start_lock, time_lock; 784 unsigned long start_lock, time_lock;
787 785
788 WARN_ON(!transaction);
789 if (is_handle_aborted(handle)) 786 if (is_handle_aborted(handle))
790 return -EROFS; 787 return -EROFS;
791 journal = transaction->t_journal; 788 journal = transaction->t_journal;
@@ -1051,7 +1048,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
1051 int err; 1048 int err;
1052 1049
1053 jbd_debug(5, "journal_head %p\n", jh); 1050 jbd_debug(5, "journal_head %p\n", jh);
1054 WARN_ON(!transaction);
1055 err = -EROFS; 1051 err = -EROFS;
1056 if (is_handle_aborted(handle)) 1052 if (is_handle_aborted(handle))
1057 goto out; 1053 goto out;
@@ -1266,7 +1262,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
1266 struct journal_head *jh; 1262 struct journal_head *jh;
1267 int ret = 0; 1263 int ret = 0;
1268 1264
1269 WARN_ON(!transaction);
1270 if (is_handle_aborted(handle)) 1265 if (is_handle_aborted(handle))
1271 return -EROFS; 1266 return -EROFS;
1272 journal = transaction->t_journal; 1267 journal = transaction->t_journal;
@@ -1397,7 +1392,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
1397 int err = 0; 1392 int err = 0;
1398 int was_modified = 0; 1393 int was_modified = 0;
1399 1394
1400 WARN_ON(!transaction);
1401 if (is_handle_aborted(handle)) 1395 if (is_handle_aborted(handle))
1402 return -EROFS; 1396 return -EROFS;
1403 journal = transaction->t_journal; 1397 journal = transaction->t_journal;
@@ -1530,8 +1524,22 @@ int jbd2_journal_stop(handle_t *handle)
1530 tid_t tid; 1524 tid_t tid;
1531 pid_t pid; 1525 pid_t pid;
1532 1526
1533 if (!transaction) 1527 if (!transaction) {
1534 goto free_and_exit; 1528 /*
1529 * Handle is already detached from the transaction so
1530 * there is nothing to do other than decrease a refcount,
1531 * or free the handle if refcount drops to zero
1532 */
1533 if (--handle->h_ref > 0) {
1534 jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
1535 handle->h_ref);
1536 return err;
1537 } else {
1538 if (handle->h_rsv_handle)
1539 jbd2_free_handle(handle->h_rsv_handle);
1540 goto free_and_exit;
1541 }
1542 }
1535 journal = transaction->t_journal; 1543 journal = transaction->t_journal;
1536 1544
1537 J_ASSERT(journal_current_handle() == handle); 1545 J_ASSERT(journal_current_handle() == handle);
@@ -2373,7 +2381,6 @@ int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode)
2373 transaction_t *transaction = handle->h_transaction; 2381 transaction_t *transaction = handle->h_transaction;
2374 journal_t *journal; 2382 journal_t *journal;
2375 2383
2376 WARN_ON(!transaction);
2377 if (is_handle_aborted(handle)) 2384 if (is_handle_aborted(handle))
2378 return -EROFS; 2385 return -EROFS;
2379 journal = transaction->t_journal; 2386 journal = transaction->t_journal;