blob: e4fdacddbb6aa0173e0e44b04c3dafc7d127f837 [file] [log] [blame]
/* This changes the definition of ucontext_t */
#define _XOPEN_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdbool.h>
#include <valgrind.h>
#define offsetof(type, fld) ((unsigned long)&((type *)0)->fld)
#define stringify(x) #x
static int verbose = 0;
#define _ASSERT_OP(a, op, b) \
do { \
unsigned long long _a = (unsigned long long)(a); \
unsigned long long _b = (unsigned long long)(b); \
if (verbose) \
fprintf(stderr, "%s:%d: ASSERT(" stringify(a) \
" " stringify(op) " " stringify(b) ")\n", \
__FILE__, __LINE__); \
if (!(_a op _b)) { \
fprintf(stderr, "%s:%d: FAILED ASSERT((" stringify(a) \
"=0x%016llx) " stringify(op) " (" stringify(b) "=0x%016llx))\n", \
__FILE__, __LINE__, _a, _b); \
_exit(1); \
} \
} while(0)
#define ASSERT_EQ(a, b) _ASSERT_OP(a, ==, b)
#define ASSERT_NE(a, b) _ASSERT_OP(a, !=, b)
#define ASSERT_LTE(a, b) _ASSERT_OP(a, <=, b)
#define ASSERT_GTE(a, b) _ASSERT_OP(a, >=, b)
#define ASSERT(e) \
do { \
if (verbose) \
fprintf(stderr, "%s:%d: ASSERT(" stringify(e) ")\n", \
__FILE__, __LINE__); \
if (!(e)) { \
fprintf(stderr, "%s:%d: FAILED ASSERT(" stringify(e) ")\n", \
__FILE__, __LINE__); \
_exit(1); \
} \
} while(0)
static bool using_int3 = false;
static volatile int sig_count = 0;
static volatile int ran_after_fault = 0;
static void *top_of_stack;
static void *bottom_of_stack;
void this_function_halts(unsigned long long a0, unsigned long long a1,
unsigned long long a2, unsigned long long a3,
unsigned long long a4, unsigned long long a5)
{
int foo;
bottom_of_stack = &foo;
/* Set up registers with known values which will be tested in the signal handler */
__asm__ volatile("movq $0xfeed01010101cafe,%rax");
__asm__ volatile("movq $0xfeed02020202cafe,%rbx");
__asm__ volatile("movq $0xfeed03030303cafe,%r10");
__asm__ volatile("movq $0xfeed04040404cafe,%r11");
__asm__ volatile("movq $0xfeed05050505cafe,%r12");
__asm__ volatile("movq $0xfeed06060606cafe,%r13");
__asm__ volatile("movq $0xfeed07070707cafe,%r14");
__asm__ volatile("movq $0xfeed08080808cafe,%r15");
__asm__ volatile("hlt");
ran_after_fault++;
}
void this_function_int3s(unsigned long long a0, unsigned long long a1,
unsigned long long a2, unsigned long long a3,
unsigned long long a4, unsigned long long a5)
{
int foo;
bottom_of_stack = &foo;
/* Set up registers with known values which will be tested in the signal handler */
__asm__ volatile("movq $0xfeed01010101cafe,%rax");
__asm__ volatile("movq $0xfeed02020202cafe,%rbx");
__asm__ volatile("movq $0xfeed03030303cafe,%r10");
__asm__ volatile("movq $0xfeed04040404cafe,%r11");
__asm__ volatile("movq $0xfeed05050505cafe,%r12");
__asm__ volatile("movq $0xfeed06060606cafe,%r13");
__asm__ volatile("movq $0xfeed07070707cafe,%r14");
__asm__ volatile("movq $0xfeed08080808cafe,%r15");
__asm__ volatile("int $3");
ran_after_fault++;
}
static void
handle_signal(int sig, siginfo_t *si, void *vuc)
{
ucontext_t *uc = (ucontext_t *)vuc;
if (verbose)
{
fprintf(stderr, "handle_signal\n");
fflush(stderr);
}
sig_count++;
ASSERT(sig_count == 1);
int expected_sig = (using_int3 ? SIGTRAP : SIGSEGV);
ASSERT_EQ(sig, expected_sig);
ASSERT_NE(si, NULL);
ASSERT_NE(uc, NULL);
ASSERT_NE(uc->uc_mcontext, NULL);
/* Test that the siginfo is set up right for this signal */
ASSERT_EQ(si->si_signo, expected_sig);
ASSERT_EQ(si->si_errno, 0);
int expected_code = (using_int3 ? 1 : 0);
ASSERT_EQ(si->si_code, expected_code);
ASSERT_EQ(si->si_pid, 0);
ASSERT_EQ(si->si_uid, 0);
ASSERT_EQ(si->si_status, 0);
ASSERT_EQ(si->si_addr, 0);
ASSERT_EQ(si->si_band, 0);
/* Test that RAX is saved to the signal ucontext */
ASSERT_EQ(uc->uc_mcontext->__ss.__rax, 0xfeed01010101cafe);
/* Test that the registers used to pass the 1st 6
* function arguments were saved in the signal ucontext */
ASSERT_EQ(uc->uc_mcontext->__ss.__rdi, 0xbabe01010101cedeULL);
ASSERT_EQ(uc->uc_mcontext->__ss.__rsi, 0xbabe02020202cedeULL);
ASSERT_EQ(uc->uc_mcontext->__ss.__rdx, 0xbabe03030303cedeULL);
ASSERT_EQ(uc->uc_mcontext->__ss.__rcx, 0xbabe04040404cedeULL);
ASSERT_EQ(uc->uc_mcontext->__ss.__r8, 0xbabe05050505cedeULL);
ASSERT_EQ(uc->uc_mcontext->__ss.__r9, 0xbabe06060606cedeULL);
/* Test that the saved RBP and RSP point into roughly the right
* part of the stack */
ASSERT_GTE(uc->uc_mcontext->__ss.__rbp, bottom_of_stack);
ASSERT_LTE(uc->uc_mcontext->__ss.__rbp, top_of_stack);
ASSERT_GTE(uc->uc_mcontext->__ss.__rsp, bottom_of_stack);
ASSERT_LTE(uc->uc_mcontext->__ss.__rsp, top_of_stack);
/* Test that the saved RIP points into roughly the
* right part of the text segment */
char *calling_fn = (using_int3 ? (char *)&this_function_int3s : (char *)&this_function_halts);
ASSERT_GTE(uc->uc_mcontext->__ss.__rip, calling_fn);
ASSERT_LTE(uc->uc_mcontext->__ss.__rip, calling_fn+400);
ASSERT_EQ(uc->uc_mcontext->__ss.__rbx, 0xfeed02020202cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r10, 0xfeed03030303cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r11, 0xfeed04040404cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r12, 0xfeed05050505cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r13, 0xfeed06060606cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r14, 0xfeed07070707cafe);
ASSERT_EQ(uc->uc_mcontext->__ss.__r15, 0xfeed08080808cafe);
/*
printf(" RFLAGS 0x%016llx\n", (unsigned long long)uc->uc_mcontext->__ss.__rflags);
*/
/*
* Test that the RIP is restored from the signal ucontext;
* this should skip past the HLT/INT instruction and
* allow execution to continue back out to main()
*/
if (verbose)
{
fprintf(stderr, "Setting up to return past the HLT\n");
fflush(stderr);
}
uc->uc_mcontext->__ss.__rip += (using_int3 ? 0 : 1);
if (verbose)
{
fprintf(stderr, "Returning from signal handler\n");
fflush(stderr);
}
}
int main(int argc, char **argv)
{
int r;
struct sigaction act;
top_of_stack = (void *)&act;
if (argc > 1 && !strcmp(argv[1], "--verbose"))
verbose = 1;
if (verbose)
printf("Setting up signal handler\n");
memset(&act, 0, sizeof(act));
act.sa_sigaction = handle_signal;
act.sa_flags |= SA_SIGINFO;
if (RUNNING_ON_VALGRIND)
using_int3 = true;
r = sigaction((using_int3 ? SIGTRAP : SIGSEGV), &act, NULL);
ASSERT_EQ(r, 0);
if (verbose)
{
fprintf(stderr, "Calling function with a breakpoint insn in it\n");
fflush(stderr);
}
if (using_int3)
this_function_int3s(0xbabe01010101cedeULL,
0xbabe02020202cedeULL,
0xbabe03030303cedeULL,
0xbabe04040404cedeULL,
0xbabe05050505cedeULL,
0xbabe06060606cedeULL);
else
this_function_halts(0xbabe01010101cedeULL,
0xbabe02020202cedeULL,
0xbabe03030303cedeULL,
0xbabe04040404cedeULL,
0xbabe05050505cedeULL,
0xbabe06060606cedeULL);
ASSERT_EQ(ran_after_fault, 1);
fprintf(stderr, "PASS\n");
return 0;
}