[PATCH 3/3] powerpc: mm: support page table check
Christophe Leroy
christophe.leroy at csgroup.eu
Mon Sep 12 16:11:42 AEST 2022
Le 12/09/2022 à 03:47, Rohan McLure a écrit :
> On creation and clearing of a page table mapping, instrument such calls
> by invoking page_table_check_pte_set and page_table_check_pte_clear
> respectively. These calls serve as a sanity check against illegal
> mappings.
>
> Enable ARCH_SUPPORTS_PAGE_TABLE_CHECK for all ppc64, and 32-bit
> platforms implementing Book3S.
Why only book3s on 32 bits ?
>
> Change pud_pfn to be a runtime bug rather than a build bug as it is
> consumed by page_table_check_pud_{clear,set} which are not called.
Runtime bug is to be banned.
>
> See also:
>
> riscv support in commit 3fee229a8eb9 ("riscv/mm: enable ARCH_SUPPORTS_PAGE_TABLE_CHECK")
Long line.
Commit lines must not be splitted in Fixes: tags, but in the rest of
message it should be.
> arm64 in commit 42b2547137f5 ("arm64/mm: enable ARCH_SUPPORTS_PAGE_TABLE_CHECK")
> x86_64 in commit d283d422c6c4 ("x86: mm: add x86_64 support for page table
> check")
>
> Signed-off-by: Rohan McLure <rmclure at linux.ibm.com>
> ---
> arch/powerpc/Kconfig | 1 +
> arch/powerpc/include/asm/book3s/32/pgtable.h | 7 ++++++-
> arch/powerpc/include/asm/book3s/64/pgtable.h | 9 ++++++++-
> arch/powerpc/include/asm/nohash/32/pgtable.h | 5 ++++-
> arch/powerpc/include/asm/nohash/64/pgtable.h | 2 ++
> arch/powerpc/include/asm/nohash/pgtable.h | 1 +
> arch/powerpc/include/asm/pgtable.h | 4 ++++
> 7 files changed, 26 insertions(+), 3 deletions(-)
>
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 4c466acdc70d..6c213ac46a92 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -149,6 +149,7 @@ config PPC
> select ARCH_STACKWALK
> select ARCH_SUPPORTS_ATOMIC_RMW
> select ARCH_SUPPORTS_DEBUG_PAGEALLOC if PPC_BOOK3S || PPC_8xx || 40x
> + select ARCH_SUPPORTS_PAGE_TABLE_CHECK
In the commit message you said only book3s on PPC32.
> select ARCH_USE_BUILTIN_BSWAP
> select ARCH_USE_CMPXCHG_LOCKREF if PPC64
> select ARCH_USE_MEMTEST
> diff --git a/arch/powerpc/include/asm/book3s/32/pgtable.h b/arch/powerpc/include/asm/book3s/32/pgtable.h
> index 40041ac713d9..3d05c8fe4604 100644
> --- a/arch/powerpc/include/asm/book3s/32/pgtable.h
> +++ b/arch/powerpc/include/asm/book3s/32/pgtable.h
> @@ -53,6 +53,8 @@
>
> #ifndef __ASSEMBLY__
>
> +#include <linux/page_table_check.h>
> +
> static inline bool pte_user(pte_t pte)
> {
> return pte_val(pte) & _PAGE_USER;
> @@ -353,7 +355,9 @@ static inline int __ptep_test_and_clear_young(struct mm_struct *mm,
> static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
> pte_t *ptep)
> {
> - return __pte(pte_update(mm, addr, ptep, ~_PAGE_HASHPTE, 0, 0));
> + unsigned long old = pte_update(mm, addr, ptep, ~_PAGE_HASHPTE, 0, 0);
Add a blank line here.
pte_update() return type is pte_basic_t, which can be more than a long
on PPC32 (see asm/page_32.h):
#ifdef CONFIG_PTE_64BIT
typedef unsigned long long pte_basic_t;
#else
typedef unsigned long pte_basic_t;
#endif
But why not just use pte_t instead, as you never use 'old' directly ?
> + page_table_check_pte_clear(mm, addr, __pte(old));
> + return __pte(old);
> }
>
> #define __HAVE_ARCH_PTEP_SET_WRPROTECT
> @@ -541,6 +545,7 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
> static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
> pte_t *ptep, pte_t pte, int percpu)
> {
> + page_table_check_pte_set(mm, addr, ptep, pte);
> #if defined(CONFIG_SMP) && !defined(CONFIG_PTE_64BIT)
> /* First case is 32-bit Hash MMU in SMP mode with 32-bit PTEs. We use the
> * helper pte_update() which does an atomic update. We need to do that
> diff --git a/arch/powerpc/include/asm/book3s/64/pgtable.h b/arch/powerpc/include/asm/book3s/64/pgtable.h
> index 8874f2a3661d..cbb7bd99c897 100644
> --- a/arch/powerpc/include/asm/book3s/64/pgtable.h
> +++ b/arch/powerpc/include/asm/book3s/64/pgtable.h
> @@ -179,6 +179,8 @@
> #define PAGE_AGP (PAGE_KERNEL_NC)
>
> #ifndef __ASSEMBLY__
> +#include <linux/page_table_check.h>
> +
> /*
> * page table defines
> */
> @@ -483,6 +485,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
> unsigned long addr, pte_t *ptep)
> {
> unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0, 0);
> + page_table_check_pte_clear(mm, addr, __pte(old));
> return __pte(old);
Same, would be better to rewrite this function and use a 'pte_t old_pte'.
> }
>
> @@ -491,12 +494,15 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
> unsigned long addr,
> pte_t *ptep, int full)
> {
> + pte_t old_pte;
You don't need that variable on the full function scope, put it inside
the { } below.
> if (full && radix_enabled()) {
> /*
> * We know that this is a full mm pte clear and
> * hence can be sure there is no parallel set_pte.
> */
> - return radix__ptep_get_and_clear_full(mm, addr, ptep, full);
> + old_pte = radix__ptep_get_and_clear_full(mm, addr, ptep, full);
> + page_table_check_pte_clear(mm, addr, old_pte);
A blank line here would increase readability.
> + return old_pte;
> }
> return ptep_get_and_clear(mm, addr, ptep);
> }
> @@ -872,6 +878,7 @@ static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
> */
> pte = __pte_raw(pte_raw(pte) | cpu_to_be64(_PAGE_PTE));
>
> + page_table_check_pte_set(mm, addr, ptep, pte);
A blank line here would increase readability as well.
> if (radix_enabled())
> return radix__set_pte_at(mm, addr, ptep, pte, percpu);
> return hash__set_pte_at(mm, addr, ptep, pte, percpu);
> diff --git a/arch/powerpc/include/asm/nohash/32/pgtable.h b/arch/powerpc/include/asm/nohash/32/pgtable.h
> index 9091e4904a6b..a3416cfb75d7 100644
> --- a/arch/powerpc/include/asm/nohash/32/pgtable.h
> +++ b/arch/powerpc/include/asm/nohash/32/pgtable.h
> @@ -166,6 +166,7 @@ void unmap_kernel_page(unsigned long va);
> #define _PAGE_CHG_MASK (PTE_RPN_MASK | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_SPECIAL)
>
> #ifndef __ASSEMBLY__
> +#include <linux/page_table_check.h>
>
> #define pte_clear(mm, addr, ptep) \
> do { pte_update(mm, addr, ptep, ~0, 0, 0); } while (0)
> @@ -305,7 +306,9 @@ static inline int __ptep_test_and_clear_young(struct mm_struct *mm,
> static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr,
> pte_t *ptep)
> {
> - return __pte(pte_update(mm, addr, ptep, ~0, 0, 0));
> + unsigned long old = pte_update(mm, addr, ptep, ~0, 0, 0);
Blank line required here.
> + page_table_check_pte_clear(mm, addr, __pte(old));
A blank line here would increase readability.
> + return __pte(old);
> }
>
> #define __HAVE_ARCH_PTEP_SET_WRPROTECT
> diff --git a/arch/powerpc/include/asm/nohash/64/pgtable.h b/arch/powerpc/include/asm/nohash/64/pgtable.h
> index 599921cc257e..5f2e10842a0d 100644
> --- a/arch/powerpc/include/asm/nohash/64/pgtable.h
> +++ b/arch/powerpc/include/asm/nohash/64/pgtable.h
> @@ -83,6 +83,7 @@
> #define H_PAGE_4K_PFN 0
>
> #ifndef __ASSEMBLY__
> +#include <linux/page_table_check.h>
> /* pte_clear moved to later in this file */
>
> static inline pte_t pte_mkwrite(pte_t pte)
> @@ -244,6 +245,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
> unsigned long addr, pte_t *ptep)
> {
> unsigned long old = pte_update(mm, addr, ptep, ~0UL, 0, 0);
Blank line.
> + page_table_check_pte_clear(mm, addr, __pte(old));
> return __pte(old);
> }
>
> diff --git a/arch/powerpc/include/asm/nohash/pgtable.h b/arch/powerpc/include/asm/nohash/pgtable.h
> index b499da6c1a99..62b221b7cccf 100644
> --- a/arch/powerpc/include/asm/nohash/pgtable.h
> +++ b/arch/powerpc/include/asm/nohash/pgtable.h
> @@ -185,6 +185,7 @@ extern void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
> static inline void __set_pte_at(struct mm_struct *mm, unsigned long addr,
> pte_t *ptep, pte_t pte, int percpu)
> {
> + page_table_check_pte_set(mm, addr, ptep, pte);
Blank line.
> /* Second case is 32-bit with 64-bit PTE. In this case, we
> * can just store as long as we do the two halves in the right order
> * with a barrier in between.
> diff --git a/arch/powerpc/include/asm/pgtable.h b/arch/powerpc/include/asm/pgtable.h
> index 8c1f5feb9360..5e1032d12499 100644
> --- a/arch/powerpc/include/asm/pgtable.h
> +++ b/arch/powerpc/include/asm/pgtable.h
> @@ -166,7 +166,11 @@ static inline int pud_pfn(pud_t pud)
> * check so this should never be used. If it grows another user we
> * want to know about it.
> */
> +#ifndef CONFIG_PAGE_TABLE_CHECK
> BUILD_BUG();
> +#else
> + BUG();
> +#endif
Awfull.
> return 0;
> }
>
More information about the Linuxppc-dev
mailing list