From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751884AbdAaLeK (ORCPT ); Tue, 31 Jan 2017 06:34:10 -0500 Received: from Galois.linutronix.de ([146.0.238.70]:34188 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750844AbdAaLdm (ORCPT ); Tue, 31 Jan 2017 06:33:42 -0500 Date: Tue, 31 Jan 2017 12:33:00 +0100 (CET) From: Thomas Gleixner To: Dmitry Vyukov cc: Al Viro , "linux-fsdevel@vger.kernel.org" , LKML , syzkaller Subject: Re: timerfd: use-after-free in timerfd_remove_cancel In-Reply-To: Message-ID: References: User-Agent: Alpine 2.20 (DEB 67 2015-01-07) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, 30 Jan 2017, Dmitry Vyukov wrote: > > Seems that ctx->might_cancel is racy. Yes, it is. Fix below. 8<------------------- --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -40,9 +40,12 @@ struct timerfd_ctx { short unsigned settime_flags; /* to show in fdinfo */ struct rcu_head rcu; struct list_head clist; - bool might_cancel; + unsigned long flags; }; +/* Bit positions for ctx->flags */ +#define MIGHT_CANCEL 0 + static LIST_HEAD(cancel_list); static DEFINE_SPINLOCK(cancel_lock); @@ -99,7 +102,7 @@ void timerfd_clock_was_set(void) rcu_read_lock(); list_for_each_entry_rcu(ctx, &cancel_list, clist) { - if (!ctx->might_cancel) + if (!test_bit(MIGHT_CANCEL, &ctx->flags)) continue; spin_lock_irqsave(&ctx->wqh.lock, flags); if (ctx->moffs != moffs) { @@ -114,8 +117,7 @@ void timerfd_clock_was_set(void) static void timerfd_remove_cancel(struct timerfd_ctx *ctx) { - if (ctx->might_cancel) { - ctx->might_cancel = false; + if (test_and_clear_bit(MIGHT_CANCEL, &ctx->flags)) { spin_lock(&cancel_lock); list_del_rcu(&ctx->clist); spin_unlock(&cancel_lock); @@ -124,7 +126,7 @@ static void timerfd_remove_cancel(struct static bool timerfd_canceled(struct timerfd_ctx *ctx) { - if (!ctx->might_cancel || ctx->moffs != KTIME_MAX) + if (!test_bit(MIGHT_CANCEL, &ctx->flags) || ctx->moffs != KTIME_MAX) return false; ctx->moffs = ktime_mono_to_real(0); return true; @@ -135,13 +137,12 @@ static void timerfd_setup_cancel(struct if ((ctx->clockid == CLOCK_REALTIME || ctx->clockid == CLOCK_REALTIME_ALARM) && (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { - if (!ctx->might_cancel) { - ctx->might_cancel = true; + if (test_and_set_bit(MIGHT_CANCEL, &ctx->flags)) { spin_lock(&cancel_lock); list_add_rcu(&ctx->clist, &cancel_list); spin_unlock(&cancel_lock); } - } else if (ctx->might_cancel) { + } else { timerfd_remove_cancel(ctx); } }