From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05D3CC47086 for ; Tue, 25 May 2021 22:07:04 +0000 (UTC) Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.kernel.org (Postfix) with ESMTP id 93F0A613FA for ; Tue, 25 May 2021 22:07:03 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 93F0A613FA Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=owner-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix) id 1ED406B006C; Tue, 25 May 2021 18:07:03 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id 19D756B006E; Tue, 25 May 2021 18:07:03 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id F30888D0001; Tue, 25 May 2021 18:07:02 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from forelay.hostedemail.com (smtprelay0198.hostedemail.com [216.40.44.198]) by kanga.kvack.org (Postfix) with ESMTP id BD7226B006C for ; Tue, 25 May 2021 18:07:02 -0400 (EDT) Received: from smtpin10.hostedemail.com (10.5.19.251.rfc1918.com [10.5.19.251]) by forelay03.hostedemail.com (Postfix) with ESMTP id 5272C8249980 for ; Tue, 25 May 2021 22:07:02 +0000 (UTC) X-FDA: 78181139484.10.1163B4F Received: from mail-ed1-f48.google.com (mail-ed1-f48.google.com [209.85.208.48]) by imf27.hostedemail.com (Postfix) with ESMTP id 83DC08019127 for ; Tue, 25 May 2021 22:06:55 +0000 (UTC) Received: by mail-ed1-f48.google.com with SMTP id j10so20512419edw.8 for ; Tue, 25 May 2021 15:07:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=e6kqazOTsKH8ppdzeGGEB7tDtO9LCIUG7IDAMF8xQf4=; b=Xii7CZFK9xIAg0CkHMV+5KHNruwiMNISkSMuyEhY6WW68VK9Ip8p0NmWLtudxDTXt8 HappXVCmlIhnXx6IMVQCYMQmhUyK8bbTV11Hj4rCHpRbKStfU4nTIMuxpeFX7aaqxWyx P8tJ7ZaPEQckDtdVerL8JsGZzOMwynhWsr+r35jjfS0vPKuEGKFG/IQauBWxrwU2j2oO 8jslft+5OpyPjg6sCaosoKW1CijGXZymQ85B+rjNV3qeK7ssyqq9IWj08FpU0kabfEoG 4x3MjR/smFCrg0ErUqlGZSpWr0RC22Zr4YE0PDisEtViLaa7UJRetcHs4xK7EXE6g0yb TN8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=e6kqazOTsKH8ppdzeGGEB7tDtO9LCIUG7IDAMF8xQf4=; b=RUjZ6Vg9G7gpe0lzlstN/mqhMemcxRWETt/Iihjn1/oakgFto3vF6YAziW8O98rv4h Yu8VdrBHVxT5znNTr5GP8H8D5kKOItPlAjwXJutFA0UNIgmun9FbWmlk+x8OHedCnkRz j9baTK2n9Dhz2voS9MsER5hJmSc9NJVQWESjvl37ViZ6vsRwReceS/rT9X4/2t2/JETM iYs4gFoMH383ga+pPio+GcjAmRnuNOiSL/VfpbM+Y6IBtawNozEV5SNuPFoSq3okvwkk 3xwmbhfHcnXDs/cGd0y9q0LKAi5FNozkqr+SDHgBOroqrxYrOkS5VUSqP0lEyht4zcbR v46Q== X-Gm-Message-State: AOAM532c2ZDqlgjWA2BOF/+8duYZlvEADGzfuWSRbGMwlqw8VLPODO66 pCtLRUMLcy7560fl6BMoNQfvynPX+xXjzOawGio= X-Google-Smtp-Source: ABdhPJxpFOpYfDmc1HjPN+Y4xCWs6g2tUWpI5rnnMHu3Pvf8UZxa5H481zpfFzxrsEvpUxuCnFtghKxt6RfN7T4IOEs= X-Received: by 2002:aa7:df96:: with SMTP id b22mr33997056edy.95.1621980420859; Tue, 25 May 2021 15:07:00 -0700 (PDT) MIME-Version: 1.0 References: <31b6c0c44cb385667287c826528ad422c6433091.1620849613.git.pcc@google.com> In-Reply-To: <31b6c0c44cb385667287c826528ad422c6433091.1620849613.git.pcc@google.com> From: Andrey Konovalov Date: Wed, 26 May 2021 01:06:45 +0300 Message-ID: Subject: Re: [PATCH v3 3/3] kasan: allow freed user page poisoning to be disabled with HW tags To: Peter Collingbourne , Jann Horn Cc: Alexander Potapenko , Catalin Marinas , Vincenzo Frascino , Andrew Morton , Evgenii Stepanov , Linux Memory Management List , linux-arm-kernel@lists.infradead.org Content-Type: text/plain; charset="UTF-8" Authentication-Results: imf27.hostedemail.com; dkim=pass header.d=gmail.com header.s=20161025 header.b=Xii7CZFK; dmarc=pass (policy=none) header.from=gmail.com; spf=pass (imf27.hostedemail.com: domain of andreyknvl@gmail.com designates 209.85.208.48 as permitted sender) smtp.mailfrom=andreyknvl@gmail.com X-Rspamd-Server: rspam05 X-Rspamd-Queue-Id: 83DC08019127 X-Stat-Signature: 1zjoniso56afs3uf8r5funpnkktorpgi X-HE-Tag: 1621980415-9617 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: +Jann On Wed, May 12, 2021 at 11:09 PM Peter Collingbourne wrote: > > Poisoning freed pages protects against kernel use-after-free. The > likelihood of such a bug involving kernel pages is significantly higher > than that for user pages. At the same time, poisoning freed pages can > impose a significant performance cost, which cannot always be justified > for user pages given the lower probability of finding a bug. Therefore, > make it possible to configure the kernel to disable freed user page > poisoning when using HW tags via the new kasan.skip_user_poison_on_free > command line option. So the potential scenario that would be undetectable with kasan.skip_user_poison_on_free enabled is: 1) kernel allocates a user page and maps it for userspace, 2) the page gets freed in the kernel, 3) kernel accesses the page leading to a use-after-free. Is this correct? If bugs involving use-after-free accesses on user pages is something that is extremely rare, perhaps we could just change the default and avoid adding a command line switch. Jann, maybe you have an idea of how common something like this is or have other inputs? Peter, is the plan to have this poisoning disabled in production? Is there an estimate on slow this is? > Signed-off-by: Peter Collingbourne > Link: https://linux-review.googlesource.com/id/I716846e2de8ef179f44e835770df7e6307be96c9 > --- > include/linux/gfp.h | 13 ++++++++++--- > include/linux/page-flags.h | 9 +++++++++ > include/trace/events/mmflags.h | 9 ++++++++- > mm/kasan/hw_tags.c | 10 ++++++++++ > mm/page_alloc.c | 12 +++++++----- > 5 files changed, 44 insertions(+), 9 deletions(-) > > diff --git a/include/linux/gfp.h b/include/linux/gfp.h > index 68ba237365dc..9a77e5660b07 100644 > --- a/include/linux/gfp.h > +++ b/include/linux/gfp.h > @@ -54,8 +54,9 @@ struct vm_area_struct; > #define ___GFP_THISNODE 0x200000u > #define ___GFP_ACCOUNT 0x400000u > #define ___GFP_ZEROTAGS 0x800000u > +#define ___GFP_SKIP_KASAN_POISON 0x1000000u > #ifdef CONFIG_LOCKDEP > -#define ___GFP_NOLOCKDEP 0x1000000u > +#define ___GFP_NOLOCKDEP 0x2000000u > #else > #define ___GFP_NOLOCKDEP 0 > #endif > @@ -233,17 +234,22 @@ struct vm_area_struct; > * > * %__GFP_ZEROTAGS returns a page with zeroed memory tags on success, if > * __GFP_ZERO is set. > + * > + * %__GFP_SKIP_KASAN_POISON returns a page which does not need to be poisoned > + * on deallocation. Typically used for userspace pages. Currently only has an > + * effect in HW tags mode, and only if a command line option is set. > */ > #define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) > #define __GFP_COMP ((__force gfp_t)___GFP_COMP) > #define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) > #define __GFP_ZEROTAGS ((__force gfp_t)___GFP_ZEROTAGS) > +#define __GFP_SKIP_KASAN_POISON ((__force gfp_t)___GFP_SKIP_KASAN_POISON) > > /* Disable lockdep for GFP context tracking */ > #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP) > > /* Room for N __GFP_FOO bits */ > -#define __GFP_BITS_SHIFT (24 + IS_ENABLED(CONFIG_LOCKDEP)) > +#define __GFP_BITS_SHIFT (25 + IS_ENABLED(CONFIG_LOCKDEP)) > #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) > > /** > @@ -320,7 +326,8 @@ struct vm_area_struct; > #define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) > #define GFP_NOIO (__GFP_RECLAIM) > #define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) > -#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL) > +#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | \ > + __GFP_HARDWALL | __GFP_SKIP_KASAN_POISON) > #define GFP_DMA __GFP_DMA > #define GFP_DMA32 __GFP_DMA32 > #define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) > diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h > index 04a34c08e0a6..40e2c5000585 100644 > --- a/include/linux/page-flags.h > +++ b/include/linux/page-flags.h > @@ -137,6 +137,9 @@ enum pageflags { > #endif > #ifdef CONFIG_64BIT > PG_arch_2, > +#endif > +#ifdef CONFIG_KASAN_HW_TAGS > + PG_skip_kasan_poison, > #endif > __NR_PAGEFLAGS, > > @@ -443,6 +446,12 @@ TESTCLEARFLAG(Young, young, PF_ANY) > PAGEFLAG(Idle, idle, PF_ANY) > #endif > > +#ifdef CONFIG_KASAN_HW_TAGS > +PAGEFLAG(SkipKASanPoison, skip_kasan_poison, PF_HEAD) > +#else > +PAGEFLAG_FALSE(SkipKASanPoison) > +#endif > + > /* > * PageReported() is used to track reported free pages within the Buddy > * allocator. We can use the non-atomic version of the test and set > diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h > index 629c7a0eaff2..390270e00a1d 100644 > --- a/include/trace/events/mmflags.h > +++ b/include/trace/events/mmflags.h > @@ -85,6 +85,12 @@ > #define IF_HAVE_PG_ARCH_2(flag,string) > #endif > > +#ifdef CONFIG_KASAN_HW_TAGS > +#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string) ,{1UL << flag, string} > +#else > +#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string) > +#endif > + > #define __def_pageflag_names \ > {1UL << PG_locked, "locked" }, \ > {1UL << PG_waiters, "waiters" }, \ > @@ -112,7 +118,8 @@ IF_HAVE_PG_UNCACHED(PG_uncached, "uncached" ) \ > IF_HAVE_PG_HWPOISON(PG_hwpoison, "hwpoison" ) \ > IF_HAVE_PG_IDLE(PG_young, "young" ) \ > IF_HAVE_PG_IDLE(PG_idle, "idle" ) \ > -IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) > +IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) \ > +IF_HAVE_PG_SKIP_KASAN_POISON(PG_skip_kasan_poison, "skip_kasan_poison") > > #define show_page_flags(flags) \ > (flags) ? __print_flags(flags, "|", \ > diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c > index 34362c8d0955..954d5c2f7683 100644 > --- a/mm/kasan/hw_tags.c > +++ b/mm/kasan/hw_tags.c > @@ -238,10 +238,20 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, > return &alloc_meta->free_track[0]; > } > > +static bool skip_user_poison_on_free; > +static int __init skip_user_poison_on_free_param(char *buf) > +{ > + return kstrtobool(buf, &skip_user_poison_on_free); > +} > +early_param("kasan.skip_user_poison_on_free", skip_user_poison_on_free_param); > + > void kasan_alloc_pages(struct page *page, unsigned int order, gfp_t flags) > { > bool init = !want_init_on_free() && want_init_on_alloc(flags); > > + if (skip_user_poison_on_free && (flags & __GFP_SKIP_KASAN_POISON)) > + SetPageSkipKASanPoison(page); > + > if (flags & __GFP_ZEROTAGS) { > int i; > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index 24e6f668ef73..2c3ac15ddd54 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -394,11 +394,12 @@ static DEFINE_STATIC_KEY_TRUE(deferred_pages); > * on-demand allocation and then freed again before the deferred pages > * initialization is done, but this is not likely to happen. > */ > -static inline bool should_skip_kasan_poison(fpi_t fpi_flags) > +static inline bool should_skip_kasan_poison(struct page *page, fpi_t fpi_flags) > { > return static_branch_unlikely(&deferred_pages) || > (!IS_ENABLED(CONFIG_KASAN_GENERIC) && > - (fpi_flags & FPI_SKIP_KASAN_POISON)); > + (fpi_flags & FPI_SKIP_KASAN_POISON)) || > + PageSkipKASanPoison(page); > } > > /* Returns true if the struct page for the pfn is uninitialised */ > @@ -449,10 +450,11 @@ defer_init(int nid, unsigned long pfn, unsigned long end_pfn) > return false; > } > #else > -static inline bool should_skip_kasan_poison(fpi_t fpi_flags) > +static inline bool should_skip_kasan_poison(struct page *page, fpi_t fpi_flags) > { > return (!IS_ENABLED(CONFIG_KASAN_GENERIC) && > - (fpi_flags & FPI_SKIP_KASAN_POISON)); > + (fpi_flags & FPI_SKIP_KASAN_POISON)) || > + PageSkipKASanPoison(page); > } > > static inline bool early_page_uninitialised(unsigned long pfn) > @@ -1244,7 +1246,7 @@ static __always_inline bool free_pages_prepare(struct page *page, > unsigned int order, bool check_free, fpi_t fpi_flags) > { > int bad = 0; > - bool skip_kasan_poison = should_skip_kasan_poison(fpi_flags); > + bool skip_kasan_poison = should_skip_kasan_poison(page, fpi_flags); > > VM_BUG_ON_PAGE(PageTail(page), page); > > -- > 2.31.1.607.g51e8a6a459-goog > From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.7 required=3.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED,DKIM_SIGNED,DKIM_VALID,FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BB18EC47086 for ; Tue, 25 May 2021 22:08:27 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7B7B6613F1 for ; Tue, 25 May 2021 22:08:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7B7B6613F1 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Cc:To:Subject:Message-ID:Date:From: In-Reply-To:References:MIME-Version:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=pTUaX19gNmcAj9KEUsVaWTIx+jJf0RsiuoIzTUivsrw=; b=lIUSRcdeOcRyEV 446oo0PIdhjL4WinNUxisxAHkWj/qSPyvk3aME/B23WRRnVlr4cYtE++0Hz1lOFVH43kG7mZs0PWB 1xHO0cO1tRcWusuZ1D7gQQ7Tz7cwdBfJ9K3o5//Ttk5/8Xi4RQ53vgJKH9waSbpTImcr5nIWp2+q/ UJfHOSQ7FfpLEx3msIF/MU71Y9BK1Ur7BsOrdcvFlHSjxkh8Lw0p9O0cZLQ28u5qbLJMURNu1OgKb Vb5b7LJTxYopICvQXgIE/TfEykH8FOosTg/vmYBH2uGg18mC1AMz8nANTpuHxWFL0qaxVko31bf4W 5uzZOm68RkNcBfQHrCkg==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1llfCd-008fbI-LB; Tue, 25 May 2021 22:07:07 +0000 Received: from mail-ed1-x531.google.com ([2a00:1450:4864:20::531]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1llfCY-008fZR-JE for linux-arm-kernel@lists.infradead.org; Tue, 25 May 2021 22:07:04 +0000 Received: by mail-ed1-x531.google.com with SMTP id b17so38096912ede.0 for ; Tue, 25 May 2021 15:07:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=e6kqazOTsKH8ppdzeGGEB7tDtO9LCIUG7IDAMF8xQf4=; b=Xii7CZFK9xIAg0CkHMV+5KHNruwiMNISkSMuyEhY6WW68VK9Ip8p0NmWLtudxDTXt8 HappXVCmlIhnXx6IMVQCYMQmhUyK8bbTV11Hj4rCHpRbKStfU4nTIMuxpeFX7aaqxWyx P8tJ7ZaPEQckDtdVerL8JsGZzOMwynhWsr+r35jjfS0vPKuEGKFG/IQauBWxrwU2j2oO 8jslft+5OpyPjg6sCaosoKW1CijGXZymQ85B+rjNV3qeK7ssyqq9IWj08FpU0kabfEoG 4x3MjR/smFCrg0ErUqlGZSpWr0RC22Zr4YE0PDisEtViLaa7UJRetcHs4xK7EXE6g0yb TN8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=e6kqazOTsKH8ppdzeGGEB7tDtO9LCIUG7IDAMF8xQf4=; b=fGSHpWlrqOwid2wYteGwzjk94q3zYa1Wy1+B/5WQcJoEd3Csf6fmn9ZVAS/EyVm+Dt 3RoDqRyeur9nOYwcBN0xYsMLwpIMeppAK0ySxKchH/abMHCn8hPG+23oPdYXcJb0gYtk VOjgga5OQcMWJDeiKFkPfO8AZ2gZ3I+VcShNtWz1UNsVTsJtzXq1pA8JTelBI9NuIwN8 7cLO8YJ+2i0uD/dL9+Cf55xedXI9QWmLBxs9zImewTIgqajTFFVo6m5na3BcOCpJ+Ih0 BG6KPBpxuAi0WOJUFvqMyImr7r0ny53kCzWjlDzHiHIce3uPLDGGvFdYKIU864/PGlwQ wm3g== X-Gm-Message-State: AOAM533v0wQ/M5x6QJxZ/UYGYkpOMgAQ7M7AtYwicOA3GRLobKgkC51h fmcCVxGC3aeiTO6zSnf9fSRFcgCs8XsCh7sDvxs= X-Google-Smtp-Source: ABdhPJxpFOpYfDmc1HjPN+Y4xCWs6g2tUWpI5rnnMHu3Pvf8UZxa5H481zpfFzxrsEvpUxuCnFtghKxt6RfN7T4IOEs= X-Received: by 2002:aa7:df96:: with SMTP id b22mr33997056edy.95.1621980420859; Tue, 25 May 2021 15:07:00 -0700 (PDT) MIME-Version: 1.0 References: <31b6c0c44cb385667287c826528ad422c6433091.1620849613.git.pcc@google.com> In-Reply-To: <31b6c0c44cb385667287c826528ad422c6433091.1620849613.git.pcc@google.com> From: Andrey Konovalov Date: Wed, 26 May 2021 01:06:45 +0300 Message-ID: Subject: Re: [PATCH v3 3/3] kasan: allow freed user page poisoning to be disabled with HW tags To: Peter Collingbourne , Jann Horn Cc: Alexander Potapenko , Catalin Marinas , Vincenzo Frascino , Andrew Morton , Evgenii Stepanov , Linux Memory Management List , linux-arm-kernel@lists.infradead.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210525_150702_680517_BA6FACB6 X-CRM114-Status: GOOD ( 35.64 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org +Jann On Wed, May 12, 2021 at 11:09 PM Peter Collingbourne wrote: > > Poisoning freed pages protects against kernel use-after-free. The > likelihood of such a bug involving kernel pages is significantly higher > than that for user pages. At the same time, poisoning freed pages can > impose a significant performance cost, which cannot always be justified > for user pages given the lower probability of finding a bug. Therefore, > make it possible to configure the kernel to disable freed user page > poisoning when using HW tags via the new kasan.skip_user_poison_on_free > command line option. So the potential scenario that would be undetectable with kasan.skip_user_poison_on_free enabled is: 1) kernel allocates a user page and maps it for userspace, 2) the page gets freed in the kernel, 3) kernel accesses the page leading to a use-after-free. Is this correct? If bugs involving use-after-free accesses on user pages is something that is extremely rare, perhaps we could just change the default and avoid adding a command line switch. Jann, maybe you have an idea of how common something like this is or have other inputs? Peter, is the plan to have this poisoning disabled in production? Is there an estimate on slow this is? > Signed-off-by: Peter Collingbourne > Link: https://linux-review.googlesource.com/id/I716846e2de8ef179f44e835770df7e6307be96c9 > --- > include/linux/gfp.h | 13 ++++++++++--- > include/linux/page-flags.h | 9 +++++++++ > include/trace/events/mmflags.h | 9 ++++++++- > mm/kasan/hw_tags.c | 10 ++++++++++ > mm/page_alloc.c | 12 +++++++----- > 5 files changed, 44 insertions(+), 9 deletions(-) > > diff --git a/include/linux/gfp.h b/include/linux/gfp.h > index 68ba237365dc..9a77e5660b07 100644 > --- a/include/linux/gfp.h > +++ b/include/linux/gfp.h > @@ -54,8 +54,9 @@ struct vm_area_struct; > #define ___GFP_THISNODE 0x200000u > #define ___GFP_ACCOUNT 0x400000u > #define ___GFP_ZEROTAGS 0x800000u > +#define ___GFP_SKIP_KASAN_POISON 0x1000000u > #ifdef CONFIG_LOCKDEP > -#define ___GFP_NOLOCKDEP 0x1000000u > +#define ___GFP_NOLOCKDEP 0x2000000u > #else > #define ___GFP_NOLOCKDEP 0 > #endif > @@ -233,17 +234,22 @@ struct vm_area_struct; > * > * %__GFP_ZEROTAGS returns a page with zeroed memory tags on success, if > * __GFP_ZERO is set. > + * > + * %__GFP_SKIP_KASAN_POISON returns a page which does not need to be poisoned > + * on deallocation. Typically used for userspace pages. Currently only has an > + * effect in HW tags mode, and only if a command line option is set. > */ > #define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) > #define __GFP_COMP ((__force gfp_t)___GFP_COMP) > #define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) > #define __GFP_ZEROTAGS ((__force gfp_t)___GFP_ZEROTAGS) > +#define __GFP_SKIP_KASAN_POISON ((__force gfp_t)___GFP_SKIP_KASAN_POISON) > > /* Disable lockdep for GFP context tracking */ > #define __GFP_NOLOCKDEP ((__force gfp_t)___GFP_NOLOCKDEP) > > /* Room for N __GFP_FOO bits */ > -#define __GFP_BITS_SHIFT (24 + IS_ENABLED(CONFIG_LOCKDEP)) > +#define __GFP_BITS_SHIFT (25 + IS_ENABLED(CONFIG_LOCKDEP)) > #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1)) > > /** > @@ -320,7 +326,8 @@ struct vm_area_struct; > #define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) > #define GFP_NOIO (__GFP_RECLAIM) > #define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) > -#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL) > +#define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS | \ > + __GFP_HARDWALL | __GFP_SKIP_KASAN_POISON) > #define GFP_DMA __GFP_DMA > #define GFP_DMA32 __GFP_DMA32 > #define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) > diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h > index 04a34c08e0a6..40e2c5000585 100644 > --- a/include/linux/page-flags.h > +++ b/include/linux/page-flags.h > @@ -137,6 +137,9 @@ enum pageflags { > #endif > #ifdef CONFIG_64BIT > PG_arch_2, > +#endif > +#ifdef CONFIG_KASAN_HW_TAGS > + PG_skip_kasan_poison, > #endif > __NR_PAGEFLAGS, > > @@ -443,6 +446,12 @@ TESTCLEARFLAG(Young, young, PF_ANY) > PAGEFLAG(Idle, idle, PF_ANY) > #endif > > +#ifdef CONFIG_KASAN_HW_TAGS > +PAGEFLAG(SkipKASanPoison, skip_kasan_poison, PF_HEAD) > +#else > +PAGEFLAG_FALSE(SkipKASanPoison) > +#endif > + > /* > * PageReported() is used to track reported free pages within the Buddy > * allocator. We can use the non-atomic version of the test and set > diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h > index 629c7a0eaff2..390270e00a1d 100644 > --- a/include/trace/events/mmflags.h > +++ b/include/trace/events/mmflags.h > @@ -85,6 +85,12 @@ > #define IF_HAVE_PG_ARCH_2(flag,string) > #endif > > +#ifdef CONFIG_KASAN_HW_TAGS > +#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string) ,{1UL << flag, string} > +#else > +#define IF_HAVE_PG_SKIP_KASAN_POISON(flag,string) > +#endif > + > #define __def_pageflag_names \ > {1UL << PG_locked, "locked" }, \ > {1UL << PG_waiters, "waiters" }, \ > @@ -112,7 +118,8 @@ IF_HAVE_PG_UNCACHED(PG_uncached, "uncached" ) \ > IF_HAVE_PG_HWPOISON(PG_hwpoison, "hwpoison" ) \ > IF_HAVE_PG_IDLE(PG_young, "young" ) \ > IF_HAVE_PG_IDLE(PG_idle, "idle" ) \ > -IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) > +IF_HAVE_PG_ARCH_2(PG_arch_2, "arch_2" ) \ > +IF_HAVE_PG_SKIP_KASAN_POISON(PG_skip_kasan_poison, "skip_kasan_poison") > > #define show_page_flags(flags) \ > (flags) ? __print_flags(flags, "|", \ > diff --git a/mm/kasan/hw_tags.c b/mm/kasan/hw_tags.c > index 34362c8d0955..954d5c2f7683 100644 > --- a/mm/kasan/hw_tags.c > +++ b/mm/kasan/hw_tags.c > @@ -238,10 +238,20 @@ struct kasan_track *kasan_get_free_track(struct kmem_cache *cache, > return &alloc_meta->free_track[0]; > } > > +static bool skip_user_poison_on_free; > +static int __init skip_user_poison_on_free_param(char *buf) > +{ > + return kstrtobool(buf, &skip_user_poison_on_free); > +} > +early_param("kasan.skip_user_poison_on_free", skip_user_poison_on_free_param); > + > void kasan_alloc_pages(struct page *page, unsigned int order, gfp_t flags) > { > bool init = !want_init_on_free() && want_init_on_alloc(flags); > > + if (skip_user_poison_on_free && (flags & __GFP_SKIP_KASAN_POISON)) > + SetPageSkipKASanPoison(page); > + > if (flags & __GFP_ZEROTAGS) { > int i; > > diff --git a/mm/page_alloc.c b/mm/page_alloc.c > index 24e6f668ef73..2c3ac15ddd54 100644 > --- a/mm/page_alloc.c > +++ b/mm/page_alloc.c > @@ -394,11 +394,12 @@ static DEFINE_STATIC_KEY_TRUE(deferred_pages); > * on-demand allocation and then freed again before the deferred pages > * initialization is done, but this is not likely to happen. > */ > -static inline bool should_skip_kasan_poison(fpi_t fpi_flags) > +static inline bool should_skip_kasan_poison(struct page *page, fpi_t fpi_flags) > { > return static_branch_unlikely(&deferred_pages) || > (!IS_ENABLED(CONFIG_KASAN_GENERIC) && > - (fpi_flags & FPI_SKIP_KASAN_POISON)); > + (fpi_flags & FPI_SKIP_KASAN_POISON)) || > + PageSkipKASanPoison(page); > } > > /* Returns true if the struct page for the pfn is uninitialised */ > @@ -449,10 +450,11 @@ defer_init(int nid, unsigned long pfn, unsigned long end_pfn) > return false; > } > #else > -static inline bool should_skip_kasan_poison(fpi_t fpi_flags) > +static inline bool should_skip_kasan_poison(struct page *page, fpi_t fpi_flags) > { > return (!IS_ENABLED(CONFIG_KASAN_GENERIC) && > - (fpi_flags & FPI_SKIP_KASAN_POISON)); > + (fpi_flags & FPI_SKIP_KASAN_POISON)) || > + PageSkipKASanPoison(page); > } > > static inline bool early_page_uninitialised(unsigned long pfn) > @@ -1244,7 +1246,7 @@ static __always_inline bool free_pages_prepare(struct page *page, > unsigned int order, bool check_free, fpi_t fpi_flags) > { > int bad = 0; > - bool skip_kasan_poison = should_skip_kasan_poison(fpi_flags); > + bool skip_kasan_poison = should_skip_kasan_poison(page, fpi_flags); > > VM_BUG_ON_PAGE(PageTail(page), page); > > -- > 2.31.1.607.g51e8a6a459-goog > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel