[PATCH v2 2/2] powerpc/mm: Warn if W+X pages found on boot

Christophe Leroy christophe.leroy at c-s.fr
Mon Jan 6 21:10:21 AEDT 2020



Le 02/05/2019 à 09:39, Russell Currey a écrit :
> Implement code to walk all pages and warn if any are found to be both
> writable and executable.  Depends on STRICT_KERNEL_RWX enabled, and is
> behind the DEBUG_WX config option.
> 
> This only runs on boot and has no runtime performance implications.
> 
> Very heavily influenced (and in some cases copied verbatim) from the
> ARM64 code written by Laura Abbott (thanks!), since our ptdump
> infrastructure is similar.
> 
> Signed-off-by: Russell Currey <ruscur at russell.cc>
> ---
> v2: A myriad of fixes and cleanups thanks to Christophe Leroy
> 
>   arch/powerpc/Kconfig.debug         | 19 ++++++++++++++
>   arch/powerpc/include/asm/pgtable.h |  6 +++++
>   arch/powerpc/mm/pgtable_32.c       |  3 +++
>   arch/powerpc/mm/pgtable_64.c       |  3 +++
>   arch/powerpc/mm/ptdump/ptdump.c    | 41 +++++++++++++++++++++++++++++-
>   5 files changed, 71 insertions(+), 1 deletion(-)
> 

[...]

> diff --git a/arch/powerpc/mm/ptdump/ptdump.c b/arch/powerpc/mm/ptdump/ptdump.c
> index a4a132f92810..e69b53a8a841 100644
> --- a/arch/powerpc/mm/ptdump/ptdump.c
> +++ b/arch/powerpc/mm/ptdump/ptdump.c
> @@ -31,7 +31,7 @@
>   #include "ptdump.h"
>   
>   #ifdef CONFIG_PPC32
> -#define KERN_VIRT_START	0
> +#define KERN_VIRT_START	PAGE_OFFSET
>   #endif
>   
>   /*
> @@ -68,6 +68,8 @@ struct pg_state {
>   	unsigned long last_pa;
>   	unsigned int level;
>   	u64 current_flags;
> +	bool check_wx;
> +	unsigned long wx_pages;
>   };
>   
>   struct addr_marker {
> @@ -177,6 +179,20 @@ static void dump_addr(struct pg_state *st, unsigned long addr)
>   
>   }
>   
> +static void note_prot_wx(struct pg_state *st, unsigned long addr)
> +{
> +	if (!st->check_wx)
> +		return;
> +
> +	if (!((st->current_flags & pgprot_val(PAGE_KERNEL_X)) == pgprot_val(PAGE_KERNEL_X)))
> +		return;
> +

I just realised that the above test is insuffisient, allthought it works 
by chance.

If I understand correctly, you want to make sure that no page is set 
with PAGE_KERNEL_X, ie that all X pages are PAGE_KERNEL_ROX

If you take the exemple of the 8xx, we have:

#define PAGE_KERNEL_X	__pgprot(_PAGE_BASE | _PAGE_KERNEL_RWX)
#define PAGE_KERNEL_ROX	__pgprot(_PAGE_BASE | _PAGE_KERNEL_ROX)

#define _PAGE_KERNEL_RWX	(_PAGE_SH | _PAGE_DIRTY | _PAGE_EXEC)
#define _PAGE_KERNEL_ROX	(_PAGE_SH | _PAGE_RO | _PAGE_EXEC)

Your test is checking which bits are set, but doesn't test which bits 
are not set. So your test only relies on the fact that _PAGE_DIRTY is 
set when the page is RW. It looks rather fragile as for some reason, a 
page might be RW without being DIRTY yet.

I think the test should be more robust, something like:

	pte_t pte = __pte(st->current_flags);

	if (!pte_exec(pte) || !pte_write(pte))
		return;

Christophe


More information about the Linuxppc-dev mailing list