#include <ucontext.h>
#include <err.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <sys/sysmacros.h>
#include <sys/debug.h>
#include <sys/x86_archext.h>
#include <sys/mman.h>
#include <unistd.h>
#include "xsave_util.h"
static void *xsave_buf;
static volatile const char *curtest;
static void
bad_success(void)
{
errx(EXIT_FAILURE, "TEST FAILED: %s setcontext, took us to success",
curtest);
}
static void
test_bad_version(ucontext_t *ctx)
{
uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
xc->ucx_vers = 23;
}
static void
test_bad_length_small(ucontext_t *ctx)
{
uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
xc->ucx_len = 0;
}
static void
test_bad_length_large(ucontext_t *ctx)
{
uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
xc->ucx_len = INT32_MAX;
}
static void
test_bad_vector(ucontext_t *ctx)
{
uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
xc->ucx_bv |= (1 << 8);
}
static void
test_context_too_short(ucontext_t *ctx)
{
uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
bcopy(xc, xsave_buf, xc->ucx_len);
ctx->uc_xsave = (long)(uintptr_t)xsave_buf;
xc = (uc_xsave_t *)ctx->uc_xsave;
xc->ucx_bv |= XFEATURE_AVX;
xc->ucx_len = sizeof (uc_xsave_t) + 0x10;
}
static void
test_context_badptr0(ucontext_t *ctx)
{
ctx->uc_xsave = 0;
}
static void
test_context_badptr1(ucontext_t *ctx)
{
void *addr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_NONE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (addr == NULL) {
err(EXIT_FAILURE, "failed to get unmapped page");
}
ctx->uc_xsave = (long)(uintptr_t)addr;
}
static void
test_context_badptr2(ucontext_t *ctx)
{
long pgsz = sysconf(_SC_PAGESIZE);
void *addr = mmap(NULL, pgsz * 2, PROT_NONE, MAP_PRIVATE | MAP_ANON,
-1, 0);
if (addr == NULL) {
errx(EXIT_FAILURE, "failed to get unmapped page");
}
if (mprotect((void *)((uintptr_t)addr + pgsz), pgsz, PROT_NONE) != 0) {
err(EXIT_FAILURE, "failed to mprotect second page");
}
ctx->uc_xsave = (uintptr_t)addr;
ctx->uc_xsave += pgsz - sizeof (uint64_t);
}
static ucontext_t *
setup_context(void)
{
ucontext_t *ctx = ucontext_alloc(0);
if (ctx == NULL) {
errx(EXIT_FAILURE, "failed to get allocate ucontext_t");
}
if (getcontext_extd(ctx, 0) != 0) {
err(EXIT_FAILURE, "failed to get extended context");
}
xsu_ustack_alloc(ctx);
makecontext(ctx, bad_success, 0);
return (ctx);
}
typedef struct {
void (*bct_func)(ucontext_t *);
const char *bct_test;
int bct_errno;
} bad_ucontext_test_t;
static const bad_ucontext_test_t tests[] = {
{ test_bad_version, "invalid version", EINVAL },
{ test_bad_length_small, "invalid length (small)", EINVAL },
{ test_bad_length_large, "invalid length (large)", EINVAL },
{ test_bad_vector, "invalid xbv", EINVAL },
{ test_context_too_short, "length does not cover AVX", EOVERFLOW },
{ test_context_badptr0, "invalid uc_xsave pointer (NULL)", EINVAL },
{ test_context_badptr1, "invalid uc_xsave pointer (unmapped page)",
EFAULT },
{ test_context_badptr2, "partially invalid uc_xsave (hit "
"unmapped page)", EFAULT },
};
int
main(int argc, char *argv[])
{
int c;
char *eptr;
unsigned long l;
const char *testno = NULL;
boolean_t do_info = B_FALSE, do_run = B_FALSE;
if (argc < 2) {
(void) fprintf(stderr, "Usage: %s [-c] [-i testno] "
"[-r testno]\n", argv[0]);
}
while ((c = getopt(argc, argv, ":ci:r:")) != -1) {
switch (c) {
case 'c':
(void) printf("%zu\n", ARRAY_SIZE(tests));
return (0);
case 'i':
testno = optarg;
do_info = B_TRUE;
break;
case 'r':
testno = optarg;
do_run = B_TRUE;
break;
case ':':
errx(EXIT_FAILURE, "Option -%c requires an operand\n",
optopt);
break;
case '?':
errx(EXIT_FAILURE, "Unknown option: -%c\n", optopt);
break;
}
}
if (testno == NULL) {
errx(EXIT_FAILURE, "one of -r and -i must be specified");
}
if (do_run && do_info) {
errx(EXIT_FAILURE, "only one of -r and -i may be specified");
}
errno = 0;
l = strtoul(testno, &eptr, 0);
if (*eptr != 0 || errno != 0) {
errx(EXIT_FAILURE, "failed to parse test number: %s", argv[1]);
}
if (l >= ARRAY_SIZE(tests)) {
errx(EXIT_FAILURE, "test number %lu is too large\n", l);
}
if (do_info) {
(void) printf("errno=%u\ndesc='%s'\n", tests[l].bct_errno,
tests[l].bct_test);
return (0);
}
xsave_buf = ucontext_alloc(0);
if (xsave_buf == NULL) {
err(EXIT_FAILURE, "failed to alternative xsave buf");
}
ucontext_t *ctx = setup_context();
VERIFY3U(ctx->uc_xsave, !=, 0);
curtest = tests[l].bct_test;
tests[l].bct_func(ctx);
(void) setcontext(ctx);
errx(EXIT_FAILURE, "TEST FAILED: setcontext returned despite us "
"expecting a core");
}