Commit c7f87f39 authored by Dave Chinner's avatar Dave Chinner Committed by Darrick J. Wong

xfs: fix use-after-free on CIL context on shutdown

xlog_wait() on the CIL context can reference a freed context if the
waiter doesn't get scheduled before the CIL context is freed. This
can happen when a task is on the hard throttle and the CIL push
aborts due to a shutdown. This was detected by generic/019:

thread 1			thread 2

__xfs_trans_commit
 xfs_log_commit_cil
  <CIL size over hard throttle limit>
  xlog_wait
   schedule
				xlog_cil_push_work
				wake_up_all
				<shutdown aborts commit>
				xlog_cil_committed
				kmem_free

   remove_wait_queue
    spin_lock_irqsave --> UAF

Fix it by moving the wait queue to the CIL rather than keeping it in
in the CIL context that gets freed on push completion. Because the
wait queue is now independent of the CIL context and we might have
multiple contexts in flight at once, only wake the waiters on the
push throttle when the context we are pushing is over the hard
throttle size threshold.

Fixes: 0e7ab7ef ("xfs: Throttle commits on delayed background CIL push")
Reported-by: default avatarYu Kuai <yukuai3@huawei.com>
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent b3a9e3b9
...@@ -671,7 +671,8 @@ xlog_cil_push_work( ...@@ -671,7 +671,8 @@ xlog_cil_push_work(
/* /*
* Wake up any background push waiters now this context is being pushed. * Wake up any background push waiters now this context is being pushed.
*/ */
wake_up_all(&ctx->push_wait); if (ctx->space_used >= XLOG_CIL_BLOCKING_SPACE_LIMIT(log))
wake_up_all(&cil->xc_push_wait);
/* /*
* Check if we've anything to push. If there is nothing, then we don't * Check if we've anything to push. If there is nothing, then we don't
...@@ -743,13 +744,12 @@ xlog_cil_push_work( ...@@ -743,13 +744,12 @@ xlog_cil_push_work(
/* /*
* initialise the new context and attach it to the CIL. Then attach * initialise the new context and attach it to the CIL. Then attach
* the current context to the CIL committing lsit so it can be found * the current context to the CIL committing list so it can be found
* during log forces to extract the commit lsn of the sequence that * during log forces to extract the commit lsn of the sequence that
* needs to be forced. * needs to be forced.
*/ */
INIT_LIST_HEAD(&new_ctx->committing); INIT_LIST_HEAD(&new_ctx->committing);
INIT_LIST_HEAD(&new_ctx->busy_extents); INIT_LIST_HEAD(&new_ctx->busy_extents);
init_waitqueue_head(&new_ctx->push_wait);
new_ctx->sequence = ctx->sequence + 1; new_ctx->sequence = ctx->sequence + 1;
new_ctx->cil = cil; new_ctx->cil = cil;
cil->xc_ctx = new_ctx; cil->xc_ctx = new_ctx;
...@@ -937,7 +937,7 @@ xlog_cil_push_background( ...@@ -937,7 +937,7 @@ xlog_cil_push_background(
if (cil->xc_ctx->space_used >= XLOG_CIL_BLOCKING_SPACE_LIMIT(log)) { if (cil->xc_ctx->space_used >= XLOG_CIL_BLOCKING_SPACE_LIMIT(log)) {
trace_xfs_log_cil_wait(log, cil->xc_ctx->ticket); trace_xfs_log_cil_wait(log, cil->xc_ctx->ticket);
ASSERT(cil->xc_ctx->space_used < log->l_logsize); ASSERT(cil->xc_ctx->space_used < log->l_logsize);
xlog_wait(&cil->xc_ctx->push_wait, &cil->xc_push_lock); xlog_wait(&cil->xc_push_wait, &cil->xc_push_lock);
return; return;
} }
...@@ -1216,12 +1216,12 @@ xlog_cil_init( ...@@ -1216,12 +1216,12 @@ xlog_cil_init(
INIT_LIST_HEAD(&cil->xc_committing); INIT_LIST_HEAD(&cil->xc_committing);
spin_lock_init(&cil->xc_cil_lock); spin_lock_init(&cil->xc_cil_lock);
spin_lock_init(&cil->xc_push_lock); spin_lock_init(&cil->xc_push_lock);
init_waitqueue_head(&cil->xc_push_wait);
init_rwsem(&cil->xc_ctx_lock); init_rwsem(&cil->xc_ctx_lock);
init_waitqueue_head(&cil->xc_commit_wait); init_waitqueue_head(&cil->xc_commit_wait);
INIT_LIST_HEAD(&ctx->committing); INIT_LIST_HEAD(&ctx->committing);
INIT_LIST_HEAD(&ctx->busy_extents); INIT_LIST_HEAD(&ctx->busy_extents);
init_waitqueue_head(&ctx->push_wait);
ctx->sequence = 1; ctx->sequence = 1;
ctx->cil = cil; ctx->cil = cil;
cil->xc_ctx = ctx; cil->xc_ctx = ctx;
......
...@@ -240,7 +240,6 @@ struct xfs_cil_ctx { ...@@ -240,7 +240,6 @@ struct xfs_cil_ctx {
struct xfs_log_vec *lv_chain; /* logvecs being pushed */ struct xfs_log_vec *lv_chain; /* logvecs being pushed */
struct list_head iclog_entry; struct list_head iclog_entry;
struct list_head committing; /* ctx committing list */ struct list_head committing; /* ctx committing list */
wait_queue_head_t push_wait; /* background push throttle */
struct work_struct discard_endio_work; struct work_struct discard_endio_work;
}; };
...@@ -274,6 +273,7 @@ struct xfs_cil { ...@@ -274,6 +273,7 @@ struct xfs_cil {
wait_queue_head_t xc_commit_wait; wait_queue_head_t xc_commit_wait;
xfs_lsn_t xc_current_sequence; xfs_lsn_t xc_current_sequence;
struct work_struct xc_push_work; struct work_struct xc_push_work;
wait_queue_head_t xc_push_wait; /* background push throttle */
} ____cacheline_aligned_in_smp; } ____cacheline_aligned_in_smp;
/* /*
......
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