| From b7a2a016a7ed67be73c81bfebf4be4686f9204f5 Mon Sep 17 00:00:00 2001 |
| From: Jens Axboe <axboe@kernel.dk> |
| Date: Mon, 3 Feb 2020 10:33:42 -0700 |
| Subject: [PATCH] aio: prevent potential eventfd recursion on poll |
| |
| commit 01d7a356872eec22ef34a33a5f9cfa917d145468 upstream. |
| |
| If we have nested or circular eventfd wakeups, then we can deadlock if |
| we run them inline from our poll waitqueue wakeup handler. It's also |
| possible to have very long chains of notifications, to the extent where |
| we could risk blowing the stack. |
| |
| Check the eventfd recursion count before calling eventfd_signal(). If |
| it's non-zero, then punt the signaling to async context. This is always |
| safe, as it takes us out-of-line in terms of stack and locking context. |
| |
| Cc: stable@vger.kernel.org # 4.19+ |
| Reviewed-by: Jeff Moyer <jmoyer@redhat.com> |
| Signed-off-by: Jens Axboe <axboe@kernel.dk> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/aio.c b/fs/aio.c |
| index 53ce4bffc289..31f2ff3c507b 100644 |
| --- a/fs/aio.c |
| +++ b/fs/aio.c |
| @@ -1611,6 +1611,14 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, |
| return 0; |
| } |
| |
| +static void aio_poll_put_work(struct work_struct *work) |
| +{ |
| + struct poll_iocb *req = container_of(work, struct poll_iocb, work); |
| + struct aio_kiocb *iocb = container_of(req, struct aio_kiocb, poll); |
| + |
| + iocb_put(iocb); |
| +} |
| + |
| static void aio_poll_complete_work(struct work_struct *work) |
| { |
| struct poll_iocb *req = container_of(work, struct poll_iocb, work); |
| @@ -1675,6 +1683,8 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, |
| list_del_init(&req->wait.entry); |
| |
| if (mask && spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { |
| + struct kioctx *ctx = iocb->ki_ctx; |
| + |
| /* |
| * Try to complete the iocb inline if we can. Use |
| * irqsave/irqrestore because not all filesystems (e.g. fuse) |
| @@ -1684,8 +1694,14 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, |
| list_del(&iocb->ki_list); |
| iocb->ki_res.res = mangle_poll(mask); |
| req->done = true; |
| - spin_unlock_irqrestore(&iocb->ki_ctx->ctx_lock, flags); |
| - iocb_put(iocb); |
| + if (iocb->ki_eventfd && eventfd_signal_count()) { |
| + iocb = NULL; |
| + INIT_WORK(&req->work, aio_poll_put_work); |
| + schedule_work(&req->work); |
| + } |
| + spin_unlock_irqrestore(&ctx->ctx_lock, flags); |
| + if (iocb) |
| + iocb_put(iocb); |
| } else { |
| schedule_work(&req->work); |
| } |
| -- |
| 2.7.4 |
| |