Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+24 −11
Original line number Diff line number Diff line
@@ -239,15 +239,23 @@ static void __put_ioctx(struct kioctx *ctx)
	call_rcu(&ctx->rcu_head, ctx_rcu_free);
}

#define get_ioctx(kioctx) do {						\
	BUG_ON(atomic_read(&(kioctx)->users) <= 0);			\
	atomic_inc(&(kioctx)->users);					\
} while (0)
#define put_ioctx(kioctx) do {						\
	BUG_ON(atomic_read(&(kioctx)->users) <= 0);			\
	if (unlikely(atomic_dec_and_test(&(kioctx)->users))) 		\
		__put_ioctx(kioctx);					\
} while (0)
static inline void get_ioctx(struct kioctx *kioctx)
{
	BUG_ON(atomic_read(&kioctx->users) <= 0);
	atomic_inc(&kioctx->users);
}

static inline int try_get_ioctx(struct kioctx *kioctx)
{
	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
 *	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)
	rcu_read_lock();

	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;
			break;
		}