1
0
Fork 0

selftests/powerpc: Add a test of bad (out-of-range) accesses

Userspace isn't allowed to access certain address ranges, make sure we
actually test that to at least some degree.

This would have caught the recent bug where the SLB fault handler was
incorrectly called on an out-of-range access when using the Radix MMU.
It also would have caught the bug we had in get_region_id() where we
were inserting SLB entries for bad addresses.

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20190520102051.12103-1-mpe@ellerman.id.au
alistair/sensors
Michael Ellerman 2019-05-20 20:20:51 +10:00
parent 1a3ec143a9
commit 5eb7cfb3a2
3 changed files with 174 additions and 1 deletions

View File

@ -5,3 +5,4 @@ prot_sao
segv_errors
wild_bctr
large_vm_fork_separation
bad_accesses

View File

@ -3,7 +3,7 @@ noarg:
$(MAKE) -C ../
TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \
large_vm_fork_separation
large_vm_fork_separation bad_accesses
TEST_GEN_PROGS_EXTENDED := tlbie_test
TEST_GEN_FILES := tempfile
@ -16,6 +16,7 @@ $(OUTPUT)/prot_sao: ../utils.c
$(OUTPUT)/wild_bctr: CFLAGS += -m64
$(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64
$(OUTPUT)/bad_accesses: CFLAGS += -m64
$(OUTPUT)/tempfile:
dd if=/dev/zero of=$@ bs=64k count=1

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2019, Michael Ellerman, IBM Corp.
//
// Test that out-of-bounds reads/writes behave as expected.
#include <setjmp.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "utils.h"
// Old distros (Ubuntu 16.04 at least) don't define this
#ifndef SEGV_BNDERR
#define SEGV_BNDERR 3
#endif
// 64-bit kernel is always here
#define PAGE_OFFSET (0xcul << 60)
static unsigned long kernel_virt_end;
static volatile int fault_code;
static volatile unsigned long fault_addr;
static jmp_buf setjmp_env;
static void segv_handler(int n, siginfo_t *info, void *ctxt_v)
{
fault_code = info->si_code;
fault_addr = (unsigned long)info->si_addr;
siglongjmp(setjmp_env, 1);
}
int bad_access(char *p, bool write)
{
char x;
fault_code = 0;
fault_addr = 0;
if (sigsetjmp(setjmp_env, 1) == 0) {
if (write)
*p = 1;
else
x = *p;
printf("Bad - no SEGV! (%c)\n", x);
return 1;
}
// If we see MAPERR that means we took a page fault rather than an SLB
// miss. We only expect to take page faults for addresses within the
// valid kernel range.
FAIL_IF(fault_code == SEGV_MAPERR && \
(fault_addr < PAGE_OFFSET || fault_addr >= kernel_virt_end));
FAIL_IF(fault_code != SEGV_MAPERR && fault_code != SEGV_BNDERR);
return 0;
}
static int using_hash_mmu(bool *using_hash)
{
char line[128];
FILE *f;
int rc;
f = fopen("/proc/cpuinfo", "r");
FAIL_IF(!f);
rc = 0;
while (fgets(line, sizeof(line), f) != NULL) {
if (strcmp(line, "MMU : Hash\n") == 0) {
*using_hash = true;
goto out;
}
if (strcmp(line, "MMU : Radix\n") == 0) {
*using_hash = false;
goto out;
}
}
rc = -1;
out:
fclose(f);
return rc;
}
static int test(void)
{
unsigned long i, j, addr, region_shift, page_shift, page_size;
struct sigaction sig;
bool hash_mmu;
sig = (struct sigaction) {
.sa_sigaction = segv_handler,
.sa_flags = SA_SIGINFO,
};
FAIL_IF(sigaction(SIGSEGV, &sig, NULL) != 0);
FAIL_IF(using_hash_mmu(&hash_mmu));
page_size = sysconf(_SC_PAGESIZE);
if (page_size == (64 * 1024))
page_shift = 16;
else
page_shift = 12;
if (page_size == (64 * 1024) || !hash_mmu) {
region_shift = 52;
// We have 7 512T regions (4 kernel linear, vmalloc, io, vmemmap)
kernel_virt_end = PAGE_OFFSET + (7 * (512ul << 40));
} else if (page_size == (4 * 1024) && hash_mmu) {
region_shift = 46;
// We have 7 64T regions (4 kernel linear, vmalloc, io, vmemmap)
kernel_virt_end = PAGE_OFFSET + (7 * (64ul << 40));
} else
FAIL_IF(true);
printf("Using %s MMU, PAGE_SIZE = %dKB start address 0x%016lx\n",
hash_mmu ? "hash" : "radix",
(1 << page_shift) >> 10,
1ul << region_shift);
// This generates access patterns like:
// 0x0010000000000000
// 0x0010000000010000
// 0x0010000000020000
// ...
// 0x0014000000000000
// 0x0018000000000000
// 0x0020000000000000
// 0x0020000000010000
// 0x0020000000020000
// ...
// 0xf400000000000000
// 0xf800000000000000
for (i = 1; i <= ((0xful << 60) >> region_shift); i++) {
for (j = page_shift - 1; j < 60; j++) {
unsigned long base, delta;
base = i << region_shift;
delta = 1ul << j;
if (delta >= base)
break;
addr = (base | delta) & ~((1 << page_shift) - 1);
FAIL_IF(bad_access((char *)addr, false));
FAIL_IF(bad_access((char *)addr, true));
}
}
return 0;
}
int main(void)
{
return test_harness(test, "bad_accesses");
}