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 volatile 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 xsu_ustack_alloc(ctx); 143 makecontext(ctx, bad_success, 0); 144 145 return (ctx); 146 } 147 148 typedef struct { 149 void (*bct_func)(ucontext_t *); 150 const char *bct_test; 151 int bct_errno; 152 } bad_ucontext_test_t; 153 154 /* 155 * Do not use single quote characters in tests below, that'll break the shell 156 * wrapper. 157 */ 158 static const bad_ucontext_test_t tests[] = { 159 { test_bad_version, "invalid version", EINVAL }, 160 { test_bad_length_small, "invalid length (small)", EINVAL }, 161 { test_bad_length_large, "invalid length (large)", EINVAL }, 162 { test_bad_vector, "invalid xbv", EINVAL }, 163 { test_context_too_short, "length does not cover AVX", EOVERFLOW }, 164 { test_context_badptr0, "invalid uc_xsave pointer (NULL)", EINVAL }, 165 { test_context_badptr1, "invalid uc_xsave pointer (unmapped page)", 166 EFAULT }, 167 { test_context_badptr2, "partially invalid uc_xsave (hit " 168 "unmapped page)", EFAULT }, 169 }; 170 171 int 172 main(int argc, char *argv[]) 173 { 174 int c; 175 char *eptr; 176 unsigned long l; 177 const char *testno = NULL; 178 boolean_t do_info = B_FALSE, do_run = B_FALSE; 179 180 if (argc < 2) { 181 (void) fprintf(stderr, "Usage: %s [-c] [-i testno] " 182 "[-r testno]\n", argv[0]); 183 } 184 185 while ((c = getopt(argc, argv, ":ci:r:")) != -1) { 186 switch (c) { 187 case 'c': 188 (void) printf("%zu\n", ARRAY_SIZE(tests)); 189 return (0); 190 case 'i': 191 testno = optarg; 192 do_info = B_TRUE; 193 break; 194 case 'r': 195 testno = optarg; 196 do_run = B_TRUE; 197 break; 198 case ':': 199 errx(EXIT_FAILURE, "Option -%c requires an operand\n", 200 optopt); 201 break; 202 case '?': 203 errx(EXIT_FAILURE, "Unknown option: -%c\n", optopt); 204 break; 205 } 206 } 207 208 if (testno == NULL) { 209 errx(EXIT_FAILURE, "one of -r and -i must be specified"); 210 } 211 212 if (do_run && do_info) { 213 errx(EXIT_FAILURE, "only one of -r and -i may be specified"); 214 } 215 216 errno = 0; 217 l = strtoul(testno, &eptr, 0); 218 if (*eptr != 0 || errno != 0) { 219 errx(EXIT_FAILURE, "failed to parse test number: %s", argv[1]); 220 } 221 222 if (l >= ARRAY_SIZE(tests)) { 223 errx(EXIT_FAILURE, "test number %lu is too large\n", l); 224 } 225 226 if (do_info) { 227 /* 228 * Output info for our wrapper shell script in a way that's not 229 * too bad to eval. 230 */ 231 (void) printf("errno=%u\ndesc='%s'\n", tests[l].bct_errno, 232 tests[l].bct_test); 233 return (0); 234 } 235 236 /* 237 * This is a little gross, but we know right now that the extended 238 * context is going to be the approximate size that we need for 239 * operations on the system. 240 */ 241 xsave_buf = ucontext_alloc(0); 242 if (xsave_buf == NULL) { 243 err(EXIT_FAILURE, "failed to alternative xsave buf"); 244 } 245 246 ucontext_t *ctx = setup_context(); 247 VERIFY3U(ctx->uc_xsave, !=, 0); 248 curtest = tests[l].bct_test; 249 tests[l].bct_func(ctx); 250 (void) setcontext(ctx); 251 errx(EXIT_FAILURE, "TEST FAILED: setcontext returned despite us " 252 "expecting a core"); 253 } 254