From patchwork Tue Jan 31 11:33:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Gleixner X-Patchwork-Id: 9546955 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E198360425 for ; Tue, 31 Jan 2017 11:33:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D49782833B for ; Tue, 31 Jan 2017 11:33:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C8ECF28375; Tue, 31 Jan 2017 11:33:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 47BC32833B for ; Tue, 31 Jan 2017 11:33:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751730AbdAaLdq (ORCPT ); Tue, 31 Jan 2017 06:33:46 -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 Received: from localhost ([127.0.0.1]) by Galois.linutronix.de with esmtps (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1cYWfg-0005Wa-Ri; Tue, 31 Jan 2017 12:32:25 +0100 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 Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On Mon, 30 Jan 2017, Dmitry Vyukov wrote: > > Seems that ctx->might_cancel is racy. Yes, it is. Fix below. 8<------------------- --- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- 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); } }