mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-27 06:34:11 +08:00
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
This commit is contained in:
parent
1a3ec143a9
commit
5eb7cfb3a2
@ -5,3 +5,4 @@ prot_sao
|
||||
segv_errors
|
||||
wild_bctr
|
||||
large_vm_fork_separation
|
||||
bad_accesses
|
||||
|
@ -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
|
||||
|
171
tools/testing/selftests/powerpc/mm/bad_accesses.c
Normal file
171
tools/testing/selftests/powerpc/mm/bad_accesses.c
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user