1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2023 Oxide Computer Company
  14  */
  15 
  16 /*
  17  * The purpose of this test is to go through and construct ucontext_t xsave
  18  * states that we expect to be invalid and therefore cause setcontext(2) to
  19  * fail. We only assume that %ymm state is present here with respect to writing
  20  * invalid tests. As if this test runs at all, that will be present.
  21  *
  22  * This is structured a little differently as we expect this program to fail to
  23  * execute and that libc will cause us to abort() if we don't correctly return
  24  * from setcontext..
  25  */
  26 
  27 #include <ucontext.h>
  28 #include <err.h>
  29 #include <stdlib.h>
  30 #include <strings.h>
  31 #include <errno.h>
  32 #include <sys/sysmacros.h>
  33 #include <sys/debug.h>
  34 #include <sys/x86_archext.h>
  35 #include <sys/mman.h>
  36 #include <unistd.h>
  37 
  38 #include "xsave_util.h"
  39 
  40 static void *xsave_buf;
  41 static const char *curtest;
  42 
  43 static void
  44 bad_success(void)
  45 {
  46         errx(EXIT_FAILURE, "TEST FAILED: %s setcontext, took us to success",
  47             curtest);
  48 }
  49 
  50 static void
  51 test_bad_version(ucontext_t *ctx)
  52 {
  53         uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
  54         xc->ucx_vers = 23;
  55 }
  56 
  57 static void
  58 test_bad_length_small(ucontext_t *ctx)
  59 {
  60         uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
  61         xc->ucx_len = 0;
  62 }
  63 
  64 static void
  65 test_bad_length_large(ucontext_t *ctx)
  66 {
  67         uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
  68         xc->ucx_len = INT32_MAX;
  69 }
  70 
  71 /*
  72  * As this can run on multiple different systems, we explicitly use bit 8 which
  73  * is reserved for a supervisor feature and so should never be valid in this
  74  * context.
  75  */
  76 static void
  77 test_bad_vector(ucontext_t *ctx)
  78 {
  79         uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
  80         xc->ucx_bv |= (1 << 8);
  81 }
  82 
  83 static void
  84 test_context_too_short(ucontext_t *ctx)
  85 {
  86         uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
  87 
  88         bcopy(xc, xsave_buf, xc->ucx_len);
  89         ctx->uc_xsave = (long)(uintptr_t)xsave_buf;
  90         xc = (uc_xsave_t *)ctx->uc_xsave;
  91         xc->ucx_bv |= XFEATURE_AVX;
  92         xc->ucx_len = sizeof (uc_xsave_t) + 0x10;
  93 }
  94 
  95 static void
  96 test_context_badptr0(ucontext_t *ctx)
  97 {
  98         ctx->uc_xsave = 0;
  99 }
 100 
 101 static void
 102 test_context_badptr1(ucontext_t *ctx)
 103 {
 104         void *addr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_NONE,
 105             MAP_PRIVATE | MAP_ANON, -1, 0);
 106         if (addr == NULL) {
 107                 err(EXIT_FAILURE, "failed to get unmapped page");
 108         }
 109 
 110         ctx->uc_xsave = (long)(uintptr_t)addr;
 111 }
 112 
 113 static void
 114 test_context_badptr2(ucontext_t *ctx)
 115 {
 116         long pgsz = sysconf(_SC_PAGESIZE);
 117         void *addr = mmap(NULL, pgsz * 2, PROT_NONE, MAP_PRIVATE | MAP_ANON,
 118             -1, 0);
 119         if (addr == NULL) {
 120                 errx(EXIT_FAILURE, "failed to get unmapped page");
 121         }
 122 
 123         if (mprotect((void *)((uintptr_t)addr + pgsz), pgsz, PROT_NONE) != 0) {
 124                 err(EXIT_FAILURE, "failed to mprotect second page");
 125         }
 126 
 127         ctx->uc_xsave = (uintptr_t)addr;
 128         ctx->uc_xsave += pgsz - sizeof (uint64_t);
 129 }
 130 
 131 static ucontext_t *
 132 setup_context(void)
 133 {
 134         ucontext_t *ctx = ucontext_alloc(0);
 135         if (ctx == NULL) {
 136                 errx(EXIT_FAILURE, "failed to get allocate ucontext_t");
 137         }
 138 
 139         if (getcontext_extd(ctx, 0) != 0) {
 140                 err(EXIT_FAILURE, "failed to get extended context");
 141         }
 142         makecontext(ctx, bad_success, 0);
 143 
 144         return (ctx);
 145 }
 146 
 147 typedef struct {
 148         void (*bct_func)(ucontext_t *);
 149         const char *bct_test;
 150         int bct_errno;
 151 } bad_ucontext_test_t;
 152 
 153 /*
 154  * Do not use single quote characters in tests below, that'll break the shell
 155  * wrapper.
 156  */
 157 static const bad_ucontext_test_t tests[] = {
 158         { test_bad_version, "invalid version", EINVAL },
 159         { test_bad_length_small, "invalid length (small)", EINVAL },
 160         { test_bad_length_large, "invalid length (large)", EINVAL },
 161         { test_bad_vector, "invalid xbv", EINVAL },
 162         { test_context_too_short, "length does not cover AVX", EOVERFLOW },
 163         { test_context_badptr0, "invalid uc_xsave pointer (NULL)", EINVAL },
 164         { test_context_badptr1, "invalid uc_xsave pointer (unmapped page)",
 165             EFAULT },
 166         { test_context_badptr2, "partially invalid uc_xsave (hit "
 167             "unmapped page)", EFAULT },
 168 };
 169 
 170 int
 171 main(int argc, char *argv[])
 172 {
 173         int c;
 174         char *eptr;
 175         unsigned long l;
 176         const char *testno = NULL;
 177         boolean_t do_info = B_FALSE, do_run = B_FALSE;
 178 
 179         if (argc < 2) {
 180                 (void) fprintf(stderr, "Usage:  %s [-c] [-i testno] "
 181                     "[-r testno]\n", argv[0]);
 182         }
 183 
 184         while ((c = getopt(argc, argv, ":ci:r:")) != -1) {
 185                 switch (c) {
 186                 case 'c':
 187                         (void) printf("%zu\n", ARRAY_SIZE(tests));
 188                         return (0);
 189                 case 'i':
 190                         testno = optarg;
 191                         do_info = B_TRUE;
 192                         break;
 193                 case 'r':
 194                         testno = optarg;
 195                         do_run = B_TRUE;
 196                         break;
 197                 case ':':
 198                         errx(EXIT_FAILURE, "Option -%c requires an operand\n",
 199                             optopt);
 200                         break;
 201                 case '?':
 202                         errx(EXIT_FAILURE, "Unknown option: -%c\n", optopt);
 203                         break;
 204                 }
 205         }
 206 
 207         if (testno == NULL) {
 208                 errx(EXIT_FAILURE, "one of -r and -i must be specified");
 209         }
 210 
 211         if (do_run && do_info) {
 212                 errx(EXIT_FAILURE, "only one of -r and -i may be specified");
 213         }
 214 
 215         errno = 0;
 216         l = strtoul(testno, &eptr, 0);
 217         if (*eptr != 0 || errno != 0) {
 218                 errx(EXIT_FAILURE, "failed to parse test number: %s", argv[1]);
 219         }
 220 
 221         if (l >= ARRAY_SIZE(tests)) {
 222                 errx(EXIT_FAILURE, "test number %lu is too large\n", l);
 223         }
 224 
 225         if (do_info) {
 226                 /*
 227                  * Output info for our wrapper shell script in a way that's not
 228                  * too bad to eval.
 229                  */
 230                 (void) printf("errno=%u\ndesc='%s'\n", tests[l].bct_errno,
 231                     tests[l].bct_test);
 232                 return (0);
 233         }
 234 
 235         /*
 236          * This is a little gross, but we know right now that the extended
 237          * context is going to be the approximate size that we need for
 238          * operations on the system.
 239          */
 240         xsave_buf = ucontext_alloc(0);
 241         if (xsave_buf == NULL) {
 242                 err(EXIT_FAILURE, "failed to alternative xsave buf");
 243         }
 244 
 245         ucontext_t *ctx = setup_context();
 246         VERIFY3U(ctx->uc_xsave, !=, 0);
 247         tests[l].bct_func(ctx);
 248         (void) setcontext(ctx);
 249         errx(EXIT_FAILURE, "TEST FAILED: setcontext returned despite us "
 250             "expecting a core");
 251 }