Currently the selftest wild_bctr can fail to build when an old gcc is used, notably on gcc using a binutils version <= 2.27, because the assembler does not support the integer suffix UL. This patch adjusts the wild_bctr test so the REG_POISON value is still treated as an unsigned long for the shifts on compilation but the UL suffix is absent on the stringification, so the inline asm code generated has no UL suffixes. Signed-off-by: Gustavo Romero <gromero@linux.vnet.ibm.com> [mpe: Wrap long line] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
171 lines
3.4 KiB
C
171 lines
3.4 KiB
C
// 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 0x5a5a
|
|
#define POISONED_REG(n) ((((unsigned long)REG_POISON) << 48) | ((n) << 32) | \
|
|
(((unsigned long)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]);
|
|
}
|
|
}
|
|
|
|
#ifdef _CALL_AIXDESC
|
|
struct opd {
|
|
unsigned long ip;
|
|
unsigned long toc;
|
|
unsigned long env;
|
|
};
|
|
static struct opd bad_opd = {
|
|
.ip = BAD_NIP,
|
|
};
|
|
#define BAD_FUNC (&bad_opd)
|
|
#else
|
|
#define BAD_FUNC BAD_NIP
|
|
#endif
|
|
|
|
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_FUNC;
|
|
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");
|
|
}
|