blob: 965f8134f9f9437fad4ad4a39c512ea801c5fa46 [file] [log] [blame]
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/prctl.h>
#include <asm/prctl.h>
#include <sys/ucontext.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <err.h>
static volatile sig_atomic_t got_sigill = 0;
extern int arch_prctl(int, unsigned long);
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");
}
#ifdef __x86_64__
# define REG_IP REG_RIP
#else
# define REG_IP REG_EIP
#endif
static void sigill(int sig, siginfo_t *si, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
got_sigill = 1;
ctx->uc_mcontext.gregs[REG_IP] += 2;
}
static int nerrs = 0;
static void test_syscall(long nr)
{
extern const char syscall_rip[];
unsigned long rcx = 1;
unsigned long orig_rcx = rcx;
unsigned short orig_ss, ss;
unsigned long orig_flags, flags;
long ret;
int local_nerrs = 0;
#ifdef __x86_64__
arch_prctl(ARCH_SET_GS, 500); // Enable segment 13
asm volatile ("mov %0,%%ss" : : "r" ((13 << 3) | 3));
#endif
got_sigill = 0;
asm volatile ("mov %%ss,%[orig_ss]\n\t"
"pushf\n\t"
"pop %[orig_flags]\n\t"
"syscall\n\t"
"syscall_rip:"
"mov %%ss,%[ss]\n\t"
"pushf\n\t"
"pop %[flags]"
: "+c" (rcx), [orig_ss] "=rm" (orig_ss), [ss] "=rm" (ss),
"=a" (ret), [flags] "=rm" (flags),
[orig_flags] "=rm" (orig_flags)
: "a" (nr) :
#ifdef __x86_64__
"r11"
#endif
);
if (got_sigill) {
printf("[OK]\tsyscall %lx: SIGILL\n", nr);
} else {
const char *outcome;
if (rcx != (unsigned long)syscall_rip) {
local_nerrs++;
outcome = "[FAIL]";
} else {
outcome = "[OK]";
}
printf("%s\tsyscall %lx: RCX = %lX RIP = %lX\n",
outcome, nr, rcx, (unsigned long)syscall_rip);
if (ret != (long)-ENOSYS && nr != SYS_fork) {
local_nerrs++;
outcome = "[FAIL]";
} else {
outcome = "[OK]";
}
printf("%s\tsyscall %lx: AX = %lx\n",
outcome, nr, ret);
printf("[NOTE]\tsyscall %lx: orig RCX = %lx ss = %hx orig_ss = %hx flags = %lx orig_flags = %lx\n",
nr, orig_rcx, ss, orig_ss, flags, orig_flags);
}
nerrs += local_nerrs;
}
static void test_int80(long nr)
{
long ret;
int local_nerrs = 0;
got_sigill = 0;
asm volatile ("int $0x80"
: "=a" (ret)
: "a" (nr)
:
);
if (got_sigill) {
printf("[OK]\tint80 %lx: SIGILL\n", nr);
} else {
const char *outcome;
if (ret != (long)-ENOSYS) {
local_nerrs++;
outcome = "[FAIL]";
} else {
outcome = "[OK]";
}
printf("%s\tint80 %lx: AX = %lx\n",
outcome, nr, ret);
}
nerrs += local_nerrs;
}
static void test_syscallfn(long nr)
{
long ret;
int local_nerrs = 0;
ret = syscall(nr);
const char *outcome;
if (ret != -1 || errno != ENOSYS) {
local_nerrs++;
outcome = "[FAIL]";
} else {
outcome = "[OK]";
}
printf("%s\tsyscall(%lx): ret = %ld, errno = %d\n",
outcome, nr, ret, errno);
nerrs += local_nerrs;
}
int main()
{
sethandler(SIGILL, sigill, 0);
test_int80(0xFFFF);
test_int80(0x4000FFFF);
test_syscall(0xFFFF);
test_syscall(0x4000FFFF);
test_syscallfn(0xFFFF);
test_syscall(SYS_fork);
return nerrs ? 1 : 0;
}