alistair23-linux/arch/powerpc/mm
Aneesh Kumar K.V 192b6a7805 powerpc/book3s64/pkeys: Fix pkey_access_permitted() for execute disable pkey
Even if the IAMR value denies execute access, the current code returns
true from pkey_access_permitted() for an execute permission check, if
the AMR read pkey bit is cleared.

This results in repeated page fault loop with a test like below:

  #define _GNU_SOURCE
  #include <errno.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <signal.h>
  #include <inttypes.h>

  #include <assert.h>
  #include <malloc.h>
  #include <unistd.h>
  #include <pthread.h>
  #include <sys/mman.h>

  #ifdef SYS_pkey_mprotect
  #undef SYS_pkey_mprotect
  #endif

  #ifdef SYS_pkey_alloc
  #undef SYS_pkey_alloc
  #endif

  #ifdef SYS_pkey_free
  #undef SYS_pkey_free
  #endif

  #undef PKEY_DISABLE_EXECUTE
  #define PKEY_DISABLE_EXECUTE	0x4

  #define SYS_pkey_mprotect	386
  #define SYS_pkey_alloc		384
  #define SYS_pkey_free		385

  #define PPC_INST_NOP		0x60000000
  #define PPC_INST_BLR		0x4e800020
  #define PROT_RWX		(PROT_READ | PROT_WRITE | PROT_EXEC)

  static int sys_pkey_mprotect(void *addr, size_t len, int prot, int pkey)
  {
  	return syscall(SYS_pkey_mprotect, addr, len, prot, pkey);
  }

  static int sys_pkey_alloc(unsigned long flags, unsigned long access_rights)
  {
  	return syscall(SYS_pkey_alloc, flags, access_rights);
  }

  static int sys_pkey_free(int pkey)
  {
  	return syscall(SYS_pkey_free, pkey);
  }

  static void do_execute(void *region)
  {
  	/* jump to region */
  	asm volatile(
  		"mtctr	%0;"
  		"bctrl"
  		: : "r"(region) : "ctr", "lr");
  }

  static void do_protect(void *region)
  {
  	size_t pgsize;
  	int i, pkey;

  	pgsize = getpagesize();

  	pkey = sys_pkey_alloc(0, PKEY_DISABLE_EXECUTE);
  	assert (pkey > 0);

  	/* perform mprotect */
  	assert(!sys_pkey_mprotect(region, pgsize, PROT_RWX, pkey));
  	do_execute(region);

  	/* free pkey */
  	assert(!sys_pkey_free(pkey));

  }

  int main(int argc, char **argv)
  {
  	size_t pgsize, numinsns;
  	unsigned int *region;
  	int i;

  	/* allocate memory region to protect */
  	pgsize = getpagesize();
  	region = memalign(pgsize, pgsize);
  	assert(region != NULL);
  	assert(!mprotect(region, pgsize, PROT_RWX));

  	/* fill page with NOPs with a BLR at the end */
  	numinsns = pgsize / sizeof(region[0]);
  	for (i = 0; i < numinsns - 1; i++)
  		region[i] = PPC_INST_NOP;
  	region[i] = PPC_INST_BLR;

  	do_protect(region);

  	return EXIT_SUCCESS;
  }

The fix is to only check the IAMR for an execute check, the AMR value
is not relevant.

Fixes: f2407ef3ba ("powerpc: helper to validate key-access permissions of a pte")
Cc: stable@vger.kernel.org # v4.16+
Reported-by: Sandipan Das <sandipan@linux.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
[mpe: Add detail to change log, tweak wording & formatting]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200712132047.1038594-1-aneesh.kumar@linux.ibm.com
2020-07-13 16:07:17 +10:00
..
book3s32 mmap locking API: convert mmap_sem comments 2020-06-09 09:39:14 -07:00
book3s64 powerpc/book3s64/pkeys: Fix pkey_access_permitted() for execute disable pkey 2020-07-13 16:07:17 +10:00
kasan mm: pgtable: add shortcuts for accessing kernel PMD and PTE 2020-06-09 09:39:13 -07:00
nohash powerpc/fsl_booke/32: Fix build with CONFIG_RANDOMIZE_BASE 2020-06-22 20:41:52 +10:00
ptdump mm: reorder includes after introduction of linux/pgtable.h 2020-06-09 09:39:13 -07:00
copro_fault.c mmap locking API: use coccinelle to convert mmap_sem rwsem call sites 2020-06-09 09:39:14 -07:00
dma-noncoherent.c dma-mapping: drop the dev argument to arch_sync_dma_for_* 2019-11-20 20:31:38 +01:00
drmem.c
fault.c mmap locking API: convert mmap_sem comments 2020-06-09 09:39:14 -07:00
highmem.c arch/kunmap_atomic: consolidate duplicate code 2020-06-04 19:06:22 -07:00
hugetlbpage.c mm: don't include asm/pgtable.h if linux/mm.h is already included 2020-06-09 09:39:13 -07:00
init-common.c mm: reorder includes after introduction of linux/pgtable.h 2020-06-09 09:39:13 -07:00
init_32.c mm: don't include asm/pgtable.h if linux/mm.h is already included 2020-06-09 09:39:13 -07:00
init_64.c mm: don't include asm/pgtable.h if linux/mm.h is already included 2020-06-09 09:39:13 -07:00
ioremap.c mm/memremap_pages: Introduce memremap_compat_align() 2020-02-20 16:58:55 -08:00
ioremap_32.c powerpc/ioremap: warn on early use of ioremap() 2019-11-19 19:38:38 +11:00
ioremap_64.c powerpc: remove __ioremap_at and __iounmap_at 2020-06-02 10:59:10 -07:00
Makefile powerpc/mm: Move ioremap functions out of pgtable_32/64.c 2019-08-27 13:03:35 +10:00
mem.c mm: don't include asm/pgtable.h if linux/mm.h is already included 2020-06-09 09:39:13 -07:00
mmap.c
mmu_context.c
mmu_decl.h powerpc/8xx: Don't set IMMR map anymore at boot 2020-05-26 22:22:21 +10:00
numa.c powerpc/numa: Remove late request for home node associativity 2020-03-04 22:44:31 +11:00
pgtable-frag.c mm: treewide: clarify pgtable_page_{ctor,dtor}() naming 2019-09-26 10:10:44 -07:00
pgtable.c arch/powerpc/mm/pgtable.c: another missed conversion 2020-06-10 14:44:46 -07:00
pgtable_32.c mm: pgtable: add shortcuts for accessing kernel PMD and PTE 2020-06-09 09:39:13 -07:00
pgtable_64.c mm: don't include asm/pgtable.h if linux/mm.h is already included 2020-06-09 09:39:13 -07:00
slice.c powerpc: Replace _ALIGN_UP() by ALIGN() 2020-05-11 23:15:15 +10:00