-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Michael Ellerman
committed
Oct 20, 2018
1 parent
0d92396
commit b7683fc
Showing
4 changed files
with
161 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ hugetlb_vs_thp_test | |
subpage_prot | ||
tempfile | ||
prot_sao | ||
segv_errors | ||
segv_errors | ||
wild_bctr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
// SPDX-License-Identifier: GPL-2.0+ | ||
/* | ||
* Copyright 2018, Michael Ellerman, IBM Corp. | ||
* | ||
* Test that an out-of-bounds branch to counter behaves as expected. | ||
*/ | ||
|
||
#include <setjmp.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/mman.h> | ||
#include <sys/types.h> | ||
#include <sys/wait.h> | ||
#include <ucontext.h> | ||
#include <unistd.h> | ||
|
||
#include "utils.h" | ||
|
||
|
||
#define BAD_NIP 0x788c545a18000000ull | ||
|
||
static struct pt_regs signal_regs; | ||
static jmp_buf setjmp_env; | ||
|
||
static void save_regs(ucontext_t *ctxt) | ||
{ | ||
struct pt_regs *regs = ctxt->uc_mcontext.regs; | ||
|
||
memcpy(&signal_regs, regs, sizeof(signal_regs)); | ||
} | ||
|
||
static void segv_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
{ | ||
save_regs(ctxt_v); | ||
longjmp(setjmp_env, 1); | ||
} | ||
|
||
static void usr2_handler(int signum, siginfo_t *info, void *ctxt_v) | ||
{ | ||
save_regs(ctxt_v); | ||
} | ||
|
||
static int ok(void) | ||
{ | ||
printf("Everything is OK in here.\n"); | ||
return 0; | ||
} | ||
|
||
#define REG_POISON 0x5a5aUL | ||
#define POISONED_REG(n) ((REG_POISON << 48) | ((n) << 32) | (REG_POISON << 16) | (n)) | ||
|
||
static inline void poison_regs(void) | ||
{ | ||
#define POISON_REG(n) \ | ||
"lis " __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
"addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" \ | ||
"sldi " __stringify(n) "," __stringify(n) ", 32 ;" \ | ||
"oris " __stringify(n) "," __stringify(n) "," __stringify(REG_POISON) ";" \ | ||
"addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" | ||
|
||
asm (POISON_REG(15) | ||
POISON_REG(16) | ||
POISON_REG(17) | ||
POISON_REG(18) | ||
POISON_REG(19) | ||
POISON_REG(20) | ||
POISON_REG(21) | ||
POISON_REG(22) | ||
POISON_REG(23) | ||
POISON_REG(24) | ||
POISON_REG(25) | ||
POISON_REG(26) | ||
POISON_REG(27) | ||
POISON_REG(28) | ||
POISON_REG(29) | ||
: // inputs | ||
: // outputs | ||
: "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", | ||
"26", "27", "28", "29" | ||
); | ||
#undef POISON_REG | ||
} | ||
|
||
static int check_regs(void) | ||
{ | ||
unsigned long i; | ||
|
||
for (i = 15; i <= 29; i++) | ||
FAIL_IF(signal_regs.gpr[i] != POISONED_REG(i)); | ||
|
||
printf("Regs OK\n"); | ||
return 0; | ||
} | ||
|
||
static void dump_regs(void) | ||
{ | ||
for (int i = 0; i < 32; i += 4) { | ||
printf("r%02d 0x%016lx r%02d 0x%016lx " \ | ||
"r%02d 0x%016lx r%02d 0x%016lx\n", | ||
i, signal_regs.gpr[i], | ||
i+1, signal_regs.gpr[i+1], | ||
i+2, signal_regs.gpr[i+2], | ||
i+3, signal_regs.gpr[i+3]); | ||
} | ||
} | ||
|
||
int test_wild_bctr(void) | ||
{ | ||
int (*func_ptr)(void); | ||
struct sigaction segv = { | ||
.sa_sigaction = segv_handler, | ||
.sa_flags = SA_SIGINFO | ||
}; | ||
struct sigaction usr2 = { | ||
.sa_sigaction = usr2_handler, | ||
.sa_flags = SA_SIGINFO | ||
}; | ||
|
||
FAIL_IF(sigaction(SIGSEGV, &segv, NULL)); | ||
FAIL_IF(sigaction(SIGUSR2, &usr2, NULL)); | ||
|
||
bzero(&signal_regs, sizeof(signal_regs)); | ||
|
||
if (setjmp(setjmp_env) == 0) { | ||
func_ptr = ok; | ||
func_ptr(); | ||
|
||
kill(getpid(), SIGUSR2); | ||
printf("Regs before:\n"); | ||
dump_regs(); | ||
bzero(&signal_regs, sizeof(signal_regs)); | ||
|
||
poison_regs(); | ||
|
||
func_ptr = (int (*)(void))BAD_NIP; | ||
func_ptr(); | ||
|
||
FAIL_IF(1); /* we didn't segv? */ | ||
} | ||
|
||
FAIL_IF(signal_regs.nip != BAD_NIP); | ||
|
||
printf("All good - took SEGV as expected branching to 0x%llx\n", BAD_NIP); | ||
|
||
dump_regs(); | ||
FAIL_IF(check_regs()); | ||
|
||
return 0; | ||
} | ||
|
||
int main(void) | ||
{ | ||
return test_harness(test_wild_bctr, "wild_bctr"); | ||
} |