Message ID | 20230119212317.8324-12-rick.p.edgecombe@intel.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp555902wrn; Thu, 19 Jan 2023 13:35:30 -0800 (PST) X-Google-Smtp-Source: AMrXdXta87p8SoCOJVxEhHTN76lUlwuTwn1yvXvH95KgbmrbpndbnioFWy0B61lrfS+QFNQsswMF X-Received: by 2002:a62:870f:0:b0:582:bb80:58d7 with SMTP id i15-20020a62870f000000b00582bb8058d7mr35287030pfe.26.1674164130562; Thu, 19 Jan 2023 13:35:30 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1674164130; cv=none; d=google.com; s=arc-20160816; b=ar+dIMdSSGWDl4nEkar0Y6gNFFrvYJmvogGjORsIw8y64JpEntVFIeJxwdmPFd8Ylh rYFEnjJG3OVC19pV8UTylT/AwdO/0nHBinemPAVMdnA0NLT7QryRHexuNx8YElIjyBvG lyHwORQ10H+DfEo0JHuuafJRW5gZWcQTehWRQtDqWyBycpHoQU0ukCDJtItLXCPBLZTg 2JPabIMbMZIGlpMus4ISbM63oWgUPKghBAqZTunknQALpn0gmXhNbGPDURO9nZFgUmes EiCcGi3LcK8fP4S1DYogs3UmKaXlHmmuwBO9Orat4BVh36Dw+SMoJBcq9JQWc2XcPs5U kWSg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:message-id:date:subject :cc:to:from:dkim-signature; bh=raQIy6Fe766NUbCocQ4XfRzcFOdeBSgY+sYH+kVPZqU=; b=SMWixnFrPMcq+jk2d5jkVx2ECLr+NnAxylaZvBIEUY7A+nEG6i1GCgGCJRBkQDUMr/ 6CvGWWlDcq4RUepCvmCU+8t4OImZLurkYVGZFEQd7aBvAND3DNzG9t+SD/uTn4mDhaGJ ZYMU+uLqHJqNfGSHHkLUZcvoNrKW3o9opF3sjLA1eSOmJXzl9XoHi+Yzj/npPWzaKEDE F0Hlc+n5OM5xcxmTOiYJd8+AuX8O/fDgNH4dvKXUayNfSw2d7wVDtXxlzsspVbuE0dRt o9Cfsm+DGTjbhVeAEU0o3PDegHyjwdAsm4yQSYdmat43By4GRTXQ3k1gnW7QccK1Yj+S ofcw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=duB0BEml; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id d19-20020a056a00199300b00575757288e4si12927932pfl.90.2023.01.19.13.35.18; Thu, 19 Jan 2023 13:35:30 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=duB0BEml; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230467AbjASVd7 (ORCPT <rfc822;pavtiger@gmail.com> + 99 others); Thu, 19 Jan 2023 16:33:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45784 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229685AbjASVb1 (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 19 Jan 2023 16:31:27 -0500 Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 616ACA8393; Thu, 19 Jan 2023 13:25:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1674163552; x=1705699552; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=0xRZR4lTYh6NOOM79Ggd51EdLXNXPAUjGASZ+kAeGKM=; b=duB0BEmlqw+ygPNkvMxSD1o+dnTZME0CnvbBTESXHyjxDrbbKpuGv4am lBKW4UPNwndUCTNGsrlGsEQNmKmAOwOJIWBP8RrPD+brqhcs1zc+tDurJ BuQkU67vPKPOeV1jIHSkWjJy+cLdLcEmc55g2vaVuk0hqnZpdxVwMAl6k 3xMdThUepL4voIWiXJjeSOb/4v/hSnV3lmRuk53yS4i5kK+bCd9uMIJ3f gZofpixT+VuWu3ryIhCgEJ0lDk6lS+E5zT5XTqPNX7V73vP6t9ANk9Tp/ Ctk17W3o+3KqGbzmxo0FF6aQS4NWDTbTCz8QoYIXSGz/chn+yoWYCfNLa A==; X-IronPort-AV: E=McAfee;i="6500,9779,10595"; a="323119409" X-IronPort-AV: E=Sophos;i="5.97,230,1669104000"; d="scan'208";a="323119409" Received: from fmsmga005.fm.intel.com ([10.253.24.32]) by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Jan 2023 13:23:42 -0800 X-IronPort-AV: E=McAfee;i="6500,9779,10595"; a="989139038" X-IronPort-AV: E=Sophos;i="5.97,230,1669104000"; d="scan'208";a="989139038" Received: from hossain3-mobl.amr.corp.intel.com (HELO rpedgeco-desk.amr.corp.intel.com) ([10.252.128.187]) by fmsmga005-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Jan 2023 13:23:41 -0800 From: Rick Edgecombe <rick.p.edgecombe@intel.com> To: x86@kernel.org, "H . Peter Anvin" <hpa@zytor.com>, Thomas Gleixner <tglx@linutronix.de>, Ingo Molnar <mingo@redhat.com>, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-mm@kvack.org, linux-arch@vger.kernel.org, linux-api@vger.kernel.org, Arnd Bergmann <arnd@arndb.de>, Andy Lutomirski <luto@kernel.org>, Balbir Singh <bsingharora@gmail.com>, Borislav Petkov <bp@alien8.de>, Cyrill Gorcunov <gorcunov@gmail.com>, Dave Hansen <dave.hansen@linux.intel.com>, Eugene Syromiatnikov <esyr@redhat.com>, Florian Weimer <fweimer@redhat.com>, "H . J . Lu" <hjl.tools@gmail.com>, Jann Horn <jannh@google.com>, Jonathan Corbet <corbet@lwn.net>, Kees Cook <keescook@chromium.org>, Mike Kravetz <mike.kravetz@oracle.com>, Nadav Amit <nadav.amit@gmail.com>, Oleg Nesterov <oleg@redhat.com>, Pavel Machek <pavel@ucw.cz>, Peter Zijlstra <peterz@infradead.org>, Randy Dunlap <rdunlap@infradead.org>, Weijiang Yang <weijiang.yang@intel.com>, "Kirill A . Shutemov" <kirill.shutemov@linux.intel.com>, John Allen <john.allen@amd.com>, kcc@google.com, eranian@google.com, rppt@kernel.org, jamorris@linux.microsoft.com, dethoma@microsoft.com, akpm@linux-foundation.org, Andrew.Cooper3@citrix.com, christina.schimpe@intel.com Cc: rick.p.edgecombe@intel.com, Yu-cheng Yu <yu-cheng.yu@intel.com> Subject: [PATCH v5 11/39] x86/mm: Update pte_modify for _PAGE_COW Date: Thu, 19 Jan 2023 13:22:49 -0800 Message-Id: <20230119212317.8324-12-rick.p.edgecombe@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20230119212317.8324-1-rick.p.edgecombe@intel.com> References: <20230119212317.8324-1-rick.p.edgecombe@intel.com> X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1755488327225099092?= X-GMAIL-MSGID: =?utf-8?q?1755488327225099092?= |
Series |
Shadow stacks for userspace
|
|
Commit Message
Edgecombe, Rick P
Jan. 19, 2023, 9:22 p.m. UTC
From: Yu-cheng Yu <yu-cheng.yu@intel.com> The Write=0,Dirty=1 PTE has been used to indicate copy-on-write pages. However, newer x86 processors also regard a Write=0,Dirty=1 PTE as a shadow stack page. In order to separate the two, the software-defined _PAGE_DIRTY is changed to _PAGE_COW for the copy-on-write case, and pte_*() are updated to do this. pte_modify() takes a "raw" pgprot_t which was not necessarily created with any of the existing PTE bit helpers. That means that it can return a pte_t with Write=0,Dirty=1, a shadow stack PTE, when it did not intend to create one. However pte_modify() changes a PTE to 'newprot', but it doesn't use the pte_*(). Modify it to also move _PAGE_DIRTY to _PAGE_COW. Do this by using the pte_mkdirty() helper. Since pte_mkdirty() also sets the soft dirty bit, extract a helper that optionally doesn't set _PAGE_SOFT_DIRTY. This helper will allow future logic for deciding when to move _PAGE_DIRTY to _PAGE_COW can live in one place. Apply the same changes to pmd_modify(). Tested-by: Pengfei Xu <pengfei.xu@intel.com> Tested-by: John Allen <john.allen@amd.com> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Co-developed-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> --- v5: - Fix pte_modify() again, to not lose _PAGE_DIRTY, but still not set _PAGE_SOFT_DIRTY as was fixed in v4. v4: - Fix an issue in soft-dirty test, where pte_modify() would detect _PAGE_COW in pte_dirty() and set the soft dirty bit in pte_mkdirty(). v2: - Update commit log with text and suggestions from (Dave Hansen) - Drop fixup_dirty_pte() in favor of clearing the HW dirty bit along with the _PAGE_CHG_MASK masking, then calling pte_mkdirty() (Dave Hansen) arch/x86/include/asm/pgtable.h | 64 +++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-)
Comments
On Thu, Jan 19, 2023 at 01:22:49PM -0800, Rick Edgecombe wrote: > From: Yu-cheng Yu <yu-cheng.yu@intel.com> > > The Write=0,Dirty=1 PTE has been used to indicate copy-on-write pages. > However, newer x86 processors also regard a Write=0,Dirty=1 PTE as a > shadow stack page. In order to separate the two, the software-defined > _PAGE_DIRTY is changed to _PAGE_COW for the copy-on-write case, and > pte_*() are updated to do this. > > pte_modify() takes a "raw" pgprot_t which was not necessarily created > with any of the existing PTE bit helpers. That means that it can return a > pte_t with Write=0,Dirty=1, a shadow stack PTE, when it did not intend to > create one. > > However pte_modify() changes a PTE to 'newprot', but it doesn't use the > pte_*(). Modify it to also move _PAGE_DIRTY to _PAGE_COW. Do this by > using the pte_mkdirty() helper. Since pte_mkdirty() also sets the soft > dirty bit, extract a helper that optionally doesn't set > _PAGE_SOFT_DIRTY. This helper will allow future logic for deciding when to > move _PAGE_DIRTY to _PAGE_COW can live in one place. > > Apply the same changes to pmd_modify(). > > Tested-by: Pengfei Xu <pengfei.xu@intel.com> > Tested-by: John Allen <john.allen@amd.com> > Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Reviewed-by: Kees Cook <keescook@chromium.org>
On Thu, Jan 19, 2023 at 01:22:49PM -0800, Rick Edgecombe wrote: > From: Yu-cheng Yu <yu-cheng.yu@intel.com> > > The Write=0,Dirty=1 PTE has been used to indicate copy-on-write pages. > However, newer x86 processors also regard a Write=0,Dirty=1 PTE as a > shadow stack page. In order to separate the two, the software-defined > _PAGE_DIRTY is changed to _PAGE_COW for the copy-on-write case, and > pte_*() are updated to do this. "In order to separate the two, change the software-defined ..." From section "2) Describe your changes" in Documentation/process/submitting-patches.rst: "Describe your changes in imperative mood, e.g. "make xyzzy do frotz" instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy to do frotz", as if you are giving orders to the codebase to change its behaviour." > +static inline pte_t __pte_mkdirty(pte_t pte, bool soft) > +{ > + pteval_t dirty = _PAGE_DIRTY; > + > + if (soft) > + dirty |= _PAGE_SOFT_DIRTY; > + > + return pte_set_flags(pte, dirty); > +} Dunno, do you even need that __pte_mkdirty() helper? AFAIU, pte_mkdirty() will always set _PAGE_SOFT_DIRTY too so whatever the __pte_mkdirty() thing needs to do, you can simply do it by foot in the two callsites. And this way you won't have the confusion: should I use pte_mkdirty() or __pte_mkdirty()? Ditto for the pmd variants. Otherwise, this is starting to make more sense now. Thx.
On Thu, 2023-02-09 at 15:08 +0100, Borislav Petkov wrote: > On Thu, Jan 19, 2023 at 01:22:49PM -0800, Rick Edgecombe wrote: > > From: Yu-cheng Yu <yu-cheng.yu@intel.com> > > > > The Write=0,Dirty=1 PTE has been used to indicate copy-on-write > > pages. > > However, newer x86 processors also regard a Write=0,Dirty=1 PTE as > > a > > shadow stack page. In order to separate the two, the software- > > defined > > _PAGE_DIRTY is changed to _PAGE_COW for the copy-on-write case, and > > pte_*() are updated to do this. > > "In order to separate the two, change the software-defined ..." > > From section "2) Describe your changes" in > Documentation/process/submitting-patches.rst: > > "Describe your changes in imperative mood, e.g. "make xyzzy do frotz" > instead of "[This patch] makes xyzzy do frotz" or "[I] changed xyzzy > to do frotz", as if you are giving orders to the codebase to change > its behaviour." Yea, this is ambiguous. It's actually trying to say that "the software- defined..." *were* changed in previous patches. I'll change it to make that clear. > > > +static inline pte_t __pte_mkdirty(pte_t pte, bool soft) > > +{ > > + pteval_t dirty = _PAGE_DIRTY; > > + > > + if (soft) > > + dirty |= _PAGE_SOFT_DIRTY; > > + > > + return pte_set_flags(pte, dirty); > > +} > > Dunno, do you even need that __pte_mkdirty() helper? > > AFAIU, pte_mkdirty() will always set _PAGE_SOFT_DIRTY too so whatever > the __pte_mkdirty() thing needs to do, you can simply do it by foot > in > the two callsites. > > And this way you won't have the confusion: should I use pte_mkdirty() > or > __pte_mkdirty()? > > Ditto for the pmd variants. > > Otherwise, this is starting to make more sense now. The thing is it would need to duplicate the pte_write() and shadow stack enablement check and know when to set the Cow(soon to be SavedDirty) bit. I see that having a similar helper is not ideal, but isn't it nice that this special critical logic for setting the Cow bit is all in one place? I actually tried it the other way, but thought that it was nicer to have a helper that might drive future people to not miss the Cow bit part. What do you think, can we leave it or give it a new name? Maybe pte_set_dirty() to be more like the x86-only pte_set_flags() family of functions? Then we have: static inline pte_t pte_mkdirty(pte_t pte) { pte = pte_set_flags(pte, _PAGE_SOFT_DIRTY); return pte_set_dirty(pte); } And... static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) ... /* * Dirty bit is not preserved above so it can be done * in a special way for the shadow stack case, where it * may need to set _PAGE_SAVED_DIRTY. __pte_mkdirty() will do * this in the case of shadow stack. */ if (oldval & _PAGE_DIRTY) pte_result = pte_set_dirty(pte_result); return pte_result; }
On Thu, Feb 09, 2023 at 05:09:15PM +0000, Edgecombe, Rick P wrote: > What do you think, can we leave it or give it a new name? Maybe > pte_set_dirty() to be more like the x86-only pte_set_flags() family of > functions? I'd do this (ontop of yours, not built, not tested, etc). It is short and sweet: pte_mkdirty() set both dirty flags pte_modifl() sets only _PAGE_DIRTY No special helpers to lookup what they do, no nothing. Plain and simple. --- diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 7942eff2af50..8ba37380966c 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -392,19 +392,10 @@ static inline pte_t pte_mkexec(pte_t pte) return pte_clear_flags(pte, _PAGE_NX); } -static inline pte_t __pte_mkdirty(pte_t pte, bool soft) -{ - pteval_t dirty = _PAGE_DIRTY; - - if (soft) - dirty |= _PAGE_SOFT_DIRTY; - - return pte_set_flags(pte, dirty); -} - +/* Set _PAGE_SOFT_DIRTY for shadow stack pages. */ static inline pte_t pte_mkdirty(pte_t pte) { - return __pte_mkdirty(pte, true); + return pte_set_flags(pte, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); } static inline pte_t pte_mkyoung(pte_t pte) @@ -749,14 +740,8 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) pte_result = __pte(val); - /* - * Dirty bit is not preserved above so it can be done - * in a special way for the shadow stack case, where it - * may need to set _PAGE_COW. __pte_mkdirty() will do this in - * the case of shadow stack. - */ if (pte_dirty(pte)) - pte_result = __pte_mkdirty(pte_result, false); + pte_result = pte_set_flags(pte_result, _PAGE_DIRTY); return pte_result; }
On Fri, 2023-02-10 at 14:57 +0100, Borislav Petkov wrote: > @@ -749,14 +740,8 @@ static inline pte_t pte_modify(pte_t pte, > pgprot_t newprot) > > pte_result = __pte(val); > > - /* > - * Dirty bit is not preserved above so it can be done > - * in a special way for the shadow stack case, where it > - * may need to set _PAGE_COW. __pte_mkdirty() will do this in > - * the case of shadow stack. > - */ > if (pte_dirty(pte)) > - pte_result = __pte_mkdirty(pte_result, false); > + pte_result = pte_set_flags(pte_result, _PAGE_DIRTY); > > return pte_result; > } > Oh, I see what you are seeing now. Did you notice that the __pte_mkdirty() logic got expanded in "x86/mm: Start actually marking _PAGE_COW"? So if we don't put that logic in a usable helper, it ends up open coded with pte_modify() looking something like this: static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pteval_t val = pte_val(pte), oldval = val; pte_t pte_result; /* * Chop off the NX bit (if present), and add the NX portion of * the newprot (if present): */ val &= (_PAGE_CHG_MASK & ~_PAGE_DIRTY); val |= check_pgprot(newprot) & ~_PAGE_CHG_MASK; val = flip_protnone_guard(oldval, val, PTE_PFN_MASK); pte_result = __pte(val); /* * Dirty bit is not preserved above so it can be done * in a special way for the shadow stack case, where it * may need to set _PAGE_SAVED_DIRTY. __pte_mkdirty() will do * this in the case of shadow stack. */ if (oldval & _PAGE_DIRTY) if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK) && !pte_write(pte_result)) pte_set_flags(pte_result, _PAGE_SAVED_DIRTY); else pte_set_flags(pte_result, _PAGE_DIRTY); } return pte_result; } So the later logic of doing the _PAGE_SAVED_DIRTY (_PAGE_COW) part is not centralized. It's ok?
On Fri, Feb 10, 2023 at 05:00:05PM +0000, Edgecombe, Rick P wrote: > /* > * Dirty bit is not preserved above so it can be done > * in a special way for the shadow stack case, where it > * may need to set _PAGE_SAVED_DIRTY. __pte_mkdirty() will do > * this in the case of shadow stack. > */ > if (oldval & _PAGE_DIRTY) > if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK) && > !pte_write(pte_result)) > pte_set_flags(pte_result, _PAGE_SAVED_DIRTY); > else > pte_set_flags(pte_result, _PAGE_DIRTY); > } > > return pte_result; > } > > So the later logic of doing the _PAGE_SAVED_DIRTY (_PAGE_COW) part is > not centralized. It's ok? I think so. 1. If you have a single pte_mkdirty() and not also a __ helper, then there's less confusion for callers as to which interface they should be using 2. The not centralized part is a single conditional so it's not like you're saving on gazillion code lines So I'd prefer that. If we end up needing this in more places then we can carve it out into a proper helper which is not in a header file such that anyone can use it but move the whole functionality into cet.c or so where we can control its visibility to the rest of the kernel. I'd say. Thx.
On Fri, 2023-02-17 at 17:11 +0100, Borislav Petkov wrote: > On Fri, Feb 10, 2023 at 05:00:05PM +0000, Edgecombe, Rick P wrote: > > /* > > * Dirty bit is not preserved above so it can be done > > * in a special way for the shadow stack case, where it > > * may need to set _PAGE_SAVED_DIRTY. __pte_mkdirty() will > > do > > * this in the case of shadow stack. > > */ > > if (oldval & _PAGE_DIRTY) > > if (cpu_feature_enabled(X86_FEATURE_USER_SHSTK) && > > !pte_write(pte_result)) > > pte_set_flags(pte_result, > > _PAGE_SAVED_DIRTY); > > else > > pte_set_flags(pte_result, _PAGE_DIRTY); > > } > > > > return pte_result; > > } > > > > So the later logic of doing the _PAGE_SAVED_DIRTY (_PAGE_COW) part > > is > > not centralized. It's ok? > > I think so. > > 1. If you have a single pte_mkdirty() and not also a __ helper, then > there's less confusion for callers as to which interface they > should be > using > > 2. The not centralized part is a single conditional so it's not like > you're saving on gazillion code lines > > So I'd prefer that. > > Fair enough, I'll adjust it. Thanks!
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 6d2f612c04b5..7942eff2af50 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -392,9 +392,19 @@ static inline pte_t pte_mkexec(pte_t pte) return pte_clear_flags(pte, _PAGE_NX); } +static inline pte_t __pte_mkdirty(pte_t pte, bool soft) +{ + pteval_t dirty = _PAGE_DIRTY; + + if (soft) + dirty |= _PAGE_SOFT_DIRTY; + + return pte_set_flags(pte, dirty); +} + static inline pte_t pte_mkdirty(pte_t pte) { - return pte_set_flags(pte, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); + return __pte_mkdirty(pte, true); } static inline pte_t pte_mkyoung(pte_t pte) @@ -503,9 +513,19 @@ static inline pmd_t pmd_wrprotect(pmd_t pmd) return pmd_clear_flags(pmd, _PAGE_RW); } +static inline pmd_t __pmd_mkdirty(pmd_t pmd, bool soft) +{ + pmdval_t dirty = _PAGE_DIRTY; + + if (soft) + dirty |= _PAGE_SOFT_DIRTY; + + return pmd_set_flags(pmd, dirty); +} + static inline pmd_t pmd_mkdirty(pmd_t pmd) { - return pmd_set_flags(pmd, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); + return __pmd_mkdirty(pmd, true); } static inline pmd_t pmd_mkdevmap(pmd_t pmd) @@ -715,26 +735,54 @@ static inline u64 flip_protnone_guard(u64 oldval, u64 val, u64 mask); static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { + pteval_t _page_chg_mask_no_dirty = _PAGE_CHG_MASK & ~_PAGE_DIRTY; pteval_t val = pte_val(pte), oldval = val; + pte_t pte_result; /* * Chop off the NX bit (if present), and add the NX portion of * the newprot (if present): */ - val &= _PAGE_CHG_MASK; - val |= check_pgprot(newprot) & ~_PAGE_CHG_MASK; + val &= _page_chg_mask_no_dirty; + val |= check_pgprot(newprot) & ~_page_chg_mask_no_dirty; val = flip_protnone_guard(oldval, val, PTE_PFN_MASK); - return __pte(val); + + pte_result = __pte(val); + + /* + * Dirty bit is not preserved above so it can be done + * in a special way for the shadow stack case, where it + * may need to set _PAGE_COW. __pte_mkdirty() will do this in + * the case of shadow stack. + */ + if (pte_dirty(pte)) + pte_result = __pte_mkdirty(pte_result, false); + + return pte_result; } static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) { + pteval_t _hpage_chg_mask_no_dirty = _HPAGE_CHG_MASK & ~_PAGE_DIRTY; pmdval_t val = pmd_val(pmd), oldval = val; + pmd_t pmd_result; - val &= _HPAGE_CHG_MASK; - val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK; + val &= _hpage_chg_mask_no_dirty; + val |= check_pgprot(newprot) & ~_hpage_chg_mask_no_dirty; val = flip_protnone_guard(oldval, val, PHYSICAL_PMD_PAGE_MASK); - return __pmd(val); + + pmd_result = __pmd(val); + + /* + * Dirty bit is not preserved above so it can be done + * in a special way for the shadow stack case, where it + * may need to set _PAGE_COW. __pmd_mkdirty() will do this in + * the case of shadow stack. + */ + if (pmd_dirty(pmd)) + pmd_result = __pmd_mkdirty(pmd_result, false); + + return pmd_result; } /*