blob: b863eab746faed491e6dbd7339e957bf6a5584fe [file] [log] [blame]
#define _GNU_SOURCE
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/signal.h>
#include <sys/ucontext.h>
#include <asm/ldt.h>
#include <err.h>
#include <setjmp.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/ptrace.h>
#include <sys/user.h>
struct selectors {
short cs, gs, fs, ss;
};
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | flags;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
static unsigned char stack16[65536] __attribute__((aligned(4096)));
asm (".pushsection .text\n\t"
".type int3, @function\n\t"
".align 4096\n\t"
"int3:\n\t"
"int3\n\t"
".size int3, . - int3\n\t"
".align 4096, 0xcc\n\t"
".popsection");
extern char int3[4096];
static void setup_ldt(void)
{
if ((unsigned long)stack16 > (1UL << 32) - sizeof(stack16))
errx(1, "stack16 is too high\n");
if ((unsigned long)int3 > (1UL << 32) - sizeof(int3))
errx(1, "int3 is too high\n");
// Borrowed from a test case by hpa
const struct user_desc code16_desc = {
.entry_number = 0,
.base_addr = (unsigned long)int3,
.limit = 4095,
.seg_32bit = 0,
.contents = 2, /* Code, not conforming */
.read_exec_only = 0,
.limit_in_pages = 0,
.seg_not_present = 0,
.useable = 0
};
const struct user_desc data16_desc = {
.entry_number = 1,
.base_addr = (unsigned long)stack16,
.limit = 0xffff,
.seg_32bit = 0,
.contents = 0, /* Data, grow-up */
.read_exec_only = 0,
.limit_in_pages = 0,
.seg_not_present = 0,
.useable = 0
};
if (syscall(SYS_modify_ldt, 1, &code16_desc, sizeof code16_desc) != 0)
err(1, "modify_ldt");
if (syscall(SYS_modify_ldt, 1, &data16_desc, sizeof data16_desc) != 0)
err(1, "modify_ldt");
}
static gregset_t initial_regs, requested_regs, resulting_regs;
static bool sig_twiddle_cs, sig_twiddle_ss;
static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS];
if (sig_twiddle_cs)
sels->cs = (0 << 3) | 7; /* LDT selector 0, RPL = 3 */
if (sig_twiddle_ss)
sels->ss = (1 << 3) | 7; /* LDT selector 1, RPL = 3 */
/* Avoid spurious failures. */
asm volatile ("mov %0,%%ds\n\t"
"mov %0,%%es\n\t"
: : "r" ((1 << 3) | 7) /* LDT selector 1, RPL = 3 */
);
ctx->uc_mcontext.gregs[REG_RIP] =
sig_twiddle_cs ? 0 : (unsigned long)&int3;
ctx->uc_mcontext.gregs[REG_RSP] = 0x8badf00d5aadc0de;
memcpy(&requested_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
return;
}
static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
struct selectors *sels = (void *)&ctx->uc_mcontext.gregs[REG_CSGSFS];
unsigned short ss;
asm ("mov %%ss,%0" : "=r" (ss));
printf("\tSIGTRAP: ss = %hx, frame ss = %hx\n", ss, sels->ss);
memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
}
static char altstack_data[SIGSTKSZ];
static int do_test(bool twiddle_cs, bool twiddle_ss)
{
printf("\ttwiddle_cs = %d, twiddle_ss = %d\n",
(int)twiddle_cs, (int)twiddle_ss);
sig_twiddle_cs = twiddle_cs;
sig_twiddle_ss = twiddle_ss;
raise(SIGUSR1);
int nerrs = 0;
for (int i = 0; i < NGREG; i++) {
greg_t req = requested_regs[i], res = resulting_regs[i];
if (i == REG_TRAPNO || i == REG_RIP)
continue; /* don't care */
if (i == REG_RSP) {
printf("\tRSP: %llx -> %llx\n", req, res);
if (res == (req & 0xFFFFFFFF))
continue; /* OK; not expected to work */
}
if (requested_regs[i] != resulting_regs[i]) {
printf("\tReg %d mismatch: requested 0x%llx; got 0x%llx\n",
i, requested_regs[i], resulting_regs[i]);
nerrs++;
}
}
if (nerrs)
printf("[FAIL]\t%d registers were corrupted\n", nerrs);
else
printf("[OK]\tall registers okay\n");
return nerrs;
}
int main()
{
int nerrs = 0;
setup_ldt();
stack_t stack = {
.ss_sp = altstack_data,
.ss_size = SIGSTKSZ,
};
if (sigaltstack(&stack, NULL) != 0)
err(1, "sigaltstack");
sethandler(SIGUSR1, sigusr1, 0);
sethandler(SIGTRAP, sigtrap, SA_ONSTACK);
nerrs += do_test(false, false);
nerrs += do_test(true, false);
nerrs += do_test(false, true);
nerrs += do_test(true, true);
return nerrs ? 1 : 0;
}