Commit 3bd9a5d7 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

aio: fix rcu ioctx lookup

aio-dio-invalidate-failure GPFs in aio_put_req from io_submit.

lookup_ioctx doesn't implement the rcu lookup pattern properly.
rcu_read_lock does not prevent refcount going to zero, so we might take
a refcount on a zero count ioctx.

Fix the bug by atomically testing for zero refcount before incrementing.

[jack@suse.cz: added comment into the code]
Reviewed-by: default avatarJeff Moyer <jmoyer@redhat.com>
Signed-off-by: default avatarNick Piggin <npiggin@kernel.dk>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 29723fcc
...@@ -239,15 +239,23 @@ static void __put_ioctx(struct kioctx *ctx) ...@@ -239,15 +239,23 @@ static void __put_ioctx(struct kioctx *ctx)
call_rcu(&ctx->rcu_head, ctx_rcu_free); call_rcu(&ctx->rcu_head, ctx_rcu_free);
} }
#define get_ioctx(kioctx) do { \ static inline void get_ioctx(struct kioctx *kioctx)
BUG_ON(atomic_read(&(kioctx)->users) <= 0); \ {
atomic_inc(&(kioctx)->users); \ BUG_ON(atomic_read(&kioctx->users) <= 0);
} while (0) atomic_inc(&kioctx->users);
#define put_ioctx(kioctx) do { \ }
BUG_ON(atomic_read(&(kioctx)->users) <= 0); \
if (unlikely(atomic_dec_and_test(&(kioctx)->users))) \ static inline int try_get_ioctx(struct kioctx *kioctx)
__put_ioctx(kioctx); \ {
} while (0) return atomic_inc_not_zero(&kioctx->users);
}
static inline void put_ioctx(struct kioctx *kioctx)
{
BUG_ON(atomic_read(&kioctx->users) <= 0);
if (unlikely(atomic_dec_and_test(&kioctx->users)))
__put_ioctx(kioctx);
}
/* ioctx_alloc /* ioctx_alloc
* Allocates and initializes an ioctx. Returns an ERR_PTR if it failed. * Allocates and initializes an ioctx. Returns an ERR_PTR if it failed.
...@@ -601,8 +609,13 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) ...@@ -601,8 +609,13 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id)
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(ctx, n, &mm->ioctx_list, list) { hlist_for_each_entry_rcu(ctx, n, &mm->ioctx_list, list) {
if (ctx->user_id == ctx_id && !ctx->dead) { /*
get_ioctx(ctx); * RCU protects us against accessing freed memory but
* we have to be careful not to get a reference when the
* reference count already dropped to 0 (ctx->dead test
* is unreliable because of races).
*/
if (ctx->user_id == ctx_id && !ctx->dead && try_get_ioctx(ctx)){
ret = ctx; ret = ctx;
break; break;
} }
......
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