diff options
| author | Michael Ellerman <mpe@ellerman.id.au> | 2018-07-24 01:53:22 -0400 |
|---|---|---|
| committer | Michael Ellerman <mpe@ellerman.id.au> | 2018-10-19 22:26:47 -0400 |
| commit | b7683fc66eba91674e52c30f4d8e596bfb5cbcf4 (patch) | |
| tree | e724c8201aa93f0d70b86ed37ee47597afefb882 | |
| parent | 0d923962ab69c27cca664a2d535e90ef655110ca (diff) | |
selftests/powerpc: Add a test of wild bctr
This tests that a bctr (Branch to counter and link), ie. a function
call, to a wildly out-of-bounds address is handled correctly.
Some old kernel versions didn't handle it correctly, see eg:
"powerpc/slb: Force a full SLB flush when we insert for a bad EA"
https://lists.ozlabs.org/pipermail/linuxppc-dev/2017-April/157397.html
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
| -rw-r--r-- | tools/testing/selftests/powerpc/include/reg.h | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/.gitignore | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/Makefile | 4 | ||||
| -rw-r--r-- | tools/testing/selftests/powerpc/mm/wild_bctr.c | 155 |
4 files changed, 161 insertions, 2 deletions
diff --git a/tools/testing/selftests/powerpc/include/reg.h b/tools/testing/selftests/powerpc/include/reg.h index 7f348c059bc2..52b4710469d2 100644 --- a/tools/testing/selftests/powerpc/include/reg.h +++ b/tools/testing/selftests/powerpc/include/reg.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | : "memory") | 17 | : "memory") |
| 18 | 18 | ||
| 19 | #define mb() asm volatile("sync" : : : "memory"); | 19 | #define mb() asm volatile("sync" : : : "memory"); |
| 20 | #define barrier() asm volatile("" : : : "memory"); | ||
| 20 | 21 | ||
| 21 | #define SPRN_MMCR2 769 | 22 | #define SPRN_MMCR2 769 |
| 22 | #define SPRN_MMCRA 770 | 23 | #define SPRN_MMCRA 770 |
diff --git a/tools/testing/selftests/powerpc/mm/.gitignore b/tools/testing/selftests/powerpc/mm/.gitignore index 7d7c42ed6de9..ba919308fe30 100644 --- a/tools/testing/selftests/powerpc/mm/.gitignore +++ b/tools/testing/selftests/powerpc/mm/.gitignore | |||
| @@ -2,4 +2,5 @@ hugetlb_vs_thp_test | |||
| 2 | subpage_prot | 2 | subpage_prot |
| 3 | tempfile | 3 | tempfile |
| 4 | prot_sao | 4 | prot_sao |
| 5 | segv_errors \ No newline at end of file | 5 | segv_errors |
| 6 | wild_bctr \ No newline at end of file | ||
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile index 33ced6e0ad25..43d68420e363 100644 --- a/tools/testing/selftests/powerpc/mm/Makefile +++ b/tools/testing/selftests/powerpc/mm/Makefile | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | noarg: | 2 | noarg: |
| 3 | $(MAKE) -C ../ | 3 | $(MAKE) -C ../ |
| 4 | 4 | ||
| 5 | TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors | 5 | TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr |
| 6 | TEST_GEN_FILES := tempfile | 6 | TEST_GEN_FILES := tempfile |
| 7 | 7 | ||
| 8 | top_srcdir = ../../../../.. | 8 | top_srcdir = ../../../../.. |
| @@ -12,6 +12,8 @@ $(TEST_GEN_PROGS): ../harness.c | |||
| 12 | 12 | ||
| 13 | $(OUTPUT)/prot_sao: ../utils.c | 13 | $(OUTPUT)/prot_sao: ../utils.c |
| 14 | 14 | ||
| 15 | $(OUTPUT)/wild_bctr: CFLAGS += -m64 | ||
| 16 | |||
| 15 | $(OUTPUT)/tempfile: | 17 | $(OUTPUT)/tempfile: |
| 16 | dd if=/dev/zero of=$@ bs=64k count=1 | 18 | dd if=/dev/zero of=$@ bs=64k count=1 |
| 17 | 19 | ||
diff --git a/tools/testing/selftests/powerpc/mm/wild_bctr.c b/tools/testing/selftests/powerpc/mm/wild_bctr.c new file mode 100644 index 000000000000..1b0e9e9a2ddc --- /dev/null +++ b/tools/testing/selftests/powerpc/mm/wild_bctr.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Copyright 2018, Michael Ellerman, IBM Corp. | ||
| 4 | * | ||
| 5 | * Test that an out-of-bounds branch to counter behaves as expected. | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <setjmp.h> | ||
| 9 | #include <stdio.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <string.h> | ||
| 12 | #include <sys/mman.h> | ||
| 13 | #include <sys/types.h> | ||
| 14 | #include <sys/wait.h> | ||
| 15 | #include <ucontext.h> | ||
| 16 | #include <unistd.h> | ||
| 17 | |||
| 18 | #include "utils.h" | ||
| 19 | |||
| 20 | |||
| 21 | #define BAD_NIP 0x788c545a18000000ull | ||
| 22 | |||
| 23 | static struct pt_regs signal_regs; | ||
| 24 | static jmp_buf setjmp_env; | ||
| 25 | |||
| 26 | static void save_regs(ucontext_t *ctxt) | ||
| 27 | { | ||
| 28 | struct pt_regs *regs = ctxt->uc_mcontext.regs; | ||
| 29 | |||
| 30 | memcpy(&signal_regs, regs, sizeof(signal_regs)); | ||
| 31 | } | ||
| 32 | |||
| 33 | static void segv_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
| 34 | { | ||
| 35 | save_regs(ctxt_v); | ||
| 36 | longjmp(setjmp_env, 1); | ||
| 37 | } | ||
| 38 | |||
| 39 | static void usr2_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
| 40 | { | ||
| 41 | save_regs(ctxt_v); | ||
| 42 | } | ||
| 43 | |||
| 44 | static int ok(void) | ||
| 45 | { | ||
| 46 | printf("Everything is OK in here.\n"); | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | #define REG_POISON 0x5a5aUL | ||
| 51 | #define POISONED_REG(n) ((REG_POISON << 48) | ((n) << 32) | (REG_POISON << 16) | (n)) | ||
| 52 | |||
| 53 | static inline void poison_regs(void) | ||
| 54 | { | ||
| 55 | #define POISON_REG(n) \ | ||
| 56 | "lis " __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
| 57 | "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" \ | ||
| 58 | "sldi " __stringify(n) "," __stringify(n) ", 32 ;" \ | ||
| 59 | "oris " __stringify(n) "," __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
| 60 | "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" | ||
| 61 | |||
| 62 | asm (POISON_REG(15) | ||
| 63 | POISON_REG(16) | ||
| 64 | POISON_REG(17) | ||
| 65 | POISON_REG(18) | ||
| 66 | POISON_REG(19) | ||
| 67 | POISON_REG(20) | ||
| 68 | POISON_REG(21) | ||
| 69 | POISON_REG(22) | ||
| 70 | POISON_REG(23) | ||
| 71 | POISON_REG(24) | ||
| 72 | POISON_REG(25) | ||
| 73 | POISON_REG(26) | ||
| 74 | POISON_REG(27) | ||
| 75 | POISON_REG(28) | ||
| 76 | POISON_REG(29) | ||
| 77 | : // inputs | ||
| 78 | : // outputs | ||
| 79 | : "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", | ||
| 80 | "26", "27", "28", "29" | ||
| 81 | ); | ||
| 82 | #undef POISON_REG | ||
| 83 | } | ||
| 84 | |||
| 85 | static int check_regs(void) | ||
| 86 | { | ||
| 87 | unsigned long i; | ||
| 88 | |||
| 89 | for (i = 15; i <= 29; i++) | ||
| 90 | FAIL_IF(signal_regs.gpr[i] != POISONED_REG(i)); | ||
| 91 | |||
| 92 | printf("Regs OK\n"); | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | static void dump_regs(void) | ||
| 97 | { | ||
| 98 | for (int i = 0; i < 32; i += 4) { | ||
| 99 | printf("r%02d 0x%016lx r%02d 0x%016lx " \ | ||
| 100 | "r%02d 0x%016lx r%02d 0x%016lx\n", | ||
| 101 | i, signal_regs.gpr[i], | ||
| 102 | i+1, signal_regs.gpr[i+1], | ||
| 103 | i+2, signal_regs.gpr[i+2], | ||
| 104 | i+3, signal_regs.gpr[i+3]); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | int test_wild_bctr(void) | ||
| 109 | { | ||
| 110 | int (*func_ptr)(void); | ||
| 111 | struct sigaction segv = { | ||
| 112 | .sa_sigaction = segv_handler, | ||
| 113 | .sa_flags = SA_SIGINFO | ||
| 114 | }; | ||
| 115 | struct sigaction usr2 = { | ||
| 116 | .sa_sigaction = usr2_handler, | ||
| 117 | .sa_flags = SA_SIGINFO | ||
| 118 | }; | ||
| 119 | |||
| 120 | FAIL_IF(sigaction(SIGSEGV, &segv, NULL)); | ||
| 121 | FAIL_IF(sigaction(SIGUSR2, &usr2, NULL)); | ||
| 122 | |||
| 123 | bzero(&signal_regs, sizeof(signal_regs)); | ||
| 124 | |||
| 125 | if (setjmp(setjmp_env) == 0) { | ||
| 126 | func_ptr = ok; | ||
| 127 | func_ptr(); | ||
| 128 | |||
| 129 | kill(getpid(), SIGUSR2); | ||
| 130 | printf("Regs before:\n"); | ||
| 131 | dump_regs(); | ||
| 132 | bzero(&signal_regs, sizeof(signal_regs)); | ||
| 133 | |||
| 134 | poison_regs(); | ||
| 135 | |||
| 136 | func_ptr = (int (*)(void))BAD_NIP; | ||
| 137 | func_ptr(); | ||
| 138 | |||
| 139 | FAIL_IF(1); /* we didn't segv? */ | ||
| 140 | } | ||
| 141 | |||
| 142 | FAIL_IF(signal_regs.nip != BAD_NIP); | ||
| 143 | |||
| 144 | printf("All good - took SEGV as expected branching to 0x%llx\n", BAD_NIP); | ||
| 145 | |||
| 146 | dump_regs(); | ||
| 147 | FAIL_IF(check_regs()); | ||
| 148 | |||
| 149 | return 0; | ||
| 150 | } | ||
| 151 | |||
| 152 | int main(void) | ||
| 153 | { | ||
| 154 | return test_harness(test_wild_bctr, "wild_bctr"); | ||
| 155 | } | ||
