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 * This attempts to do a series of writes to the /proc control file that have 18 * invalid data for the xregs state. The way that this works is that we create a 19 * thread that will be detached and just sleeps whenever it wakes up. We direct 20 * this thread to stop with a directed PCSTOP via libproc. 21 */ 22 23 #include <err.h> 24 #include <stdlib.h> 25 #include <libproc.h> 26 #include <thread.h> 27 #include <errno.h> 28 #include <string.h> 29 #include <sys/sysmacros.h> 30 #include <sys/debug.h> 31 #include <sys/x86_archext.h> 32 33 #include "xsave_util.h" 34 35 static prxregset_t *bad_xregs_pxr; 36 static size_t bad_xregs_size; 37 38 typedef struct bad_xregs_test { 39 const char *bxt_desc; 40 int bxt_errno; 41 uint32_t bxt_min; 42 void (*bxt_setup)(void **, size_t *); 43 } bad_xregs_test_t; 44 45 static void 46 bad_xregs_no_data(void **bufp, size_t *sizep) 47 { 48 *bufp = NULL; 49 *sizep = 0; 50 } 51 52 static void 53 bad_xregs_null_buf(void **bufp, size_t *sizep) 54 { 55 *bufp = NULL; 56 *sizep = sizeof (prxregset_hdr_t); 57 } 58 59 static void 60 bad_xregs_short_hdr(void **bufp, size_t *sizep) 61 { 62 prxregset_hdr_t *hdr = calloc(1, sizeof (prxregset_hdr_t)); 63 if (hdr == NULL) { 64 err(EXIT_FAILURE, "failed to allocate header"); 65 } 66 67 hdr->pr_type = PR_TYPE_XSAVE; 68 hdr->pr_size = sizeof (prxregset_hdr_t); 69 70 *bufp = hdr; 71 *sizep = sizeof (prxregset_hdr_t) - 4; 72 } 73 74 static void 75 bad_xregs_hdr_too_large(void **bufp, size_t *sizep) 76 { 77 uint32_t large = 32 * 1024 * 1024; /* 4 MiB */ 78 prxregset_hdr_t *hdr = malloc(32 * 1024 * 1024); 79 if (hdr == NULL) { 80 err(EXIT_FAILURE, "failed to allocate regset"); 81 } 82 83 (void) memcpy(hdr, bad_xregs_pxr, bad_xregs_size); 84 hdr->pr_size = large; 85 86 *bufp = hdr; 87 *sizep = large; 88 } 89 90 static prxregset_hdr_t * 91 bad_xregs_std_init(void **bufp, size_t *sizep) 92 { 93 prxregset_hdr_t *hdr = malloc(bad_xregs_size); 94 if (hdr == NULL) { 95 err(EXIT_FAILURE, "failed to allocate regset"); 96 } 97 98 (void) memcpy(hdr, bad_xregs_pxr, bad_xregs_size); 99 100 *bufp = hdr; 101 *sizep = bad_xregs_size; 102 return (hdr); 103 } 104 105 static void 106 bad_xregs_missing_data(void **bufp, size_t *sizep) 107 { 108 (void) bad_xregs_std_init(bufp, sizep); 109 *sizep /= 2; 110 } 111 112 static void 113 bad_xregs_hdr_bad_type(void **bufp, size_t *sizep) 114 { 115 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 116 hdr->pr_type = PR_TYPE_XSAVE + 167; 117 } 118 119 static void 120 bad_xregs_hdr_bad_flags(void **bufp, size_t *sizep) 121 { 122 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 123 hdr->pr_flags = 0x123; 124 } 125 126 static void 127 bad_xregs_hdr_bad_pad0(void **bufp, size_t *sizep) 128 { 129 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 130 hdr->pr_pad[0] = 0x456; 131 } 132 133 static void 134 bad_xregs_hdr_bad_pad1(void **bufp, size_t *sizep) 135 { 136 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 137 hdr->pr_pad[1] = 0x789; 138 } 139 140 static void 141 bad_xregs_hdr_bad_pad2(void **bufp, size_t *sizep) 142 { 143 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 144 hdr->pr_pad[2] = 0xabc; 145 } 146 147 static void 148 bad_xregs_hdr_bad_pad3(void **bufp, size_t *sizep) 149 { 150 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 151 hdr->pr_pad[3] = 0xdef; 152 } 153 154 static void 155 bad_xregs_hdr_no_info(void **bufp, size_t *sizep) 156 { 157 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 158 hdr->pr_ninfo = 0; 159 } 160 161 static void 162 bad_xregs_hdr_no_info_len(void **bufp, size_t *sizep) 163 { 164 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 165 uint32_t len = sizeof (prxregset_hdr_t) + sizeof (prxregset_info_t) * 166 hdr->pr_ninfo; 167 hdr->pr_size = len - 4; 168 } 169 170 static void 171 bad_xregs_info_type(void **bufp, size_t *sizep) 172 { 173 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 174 hdr->pr_info[0].pri_type = 0xbaddcafe; 175 } 176 177 static void 178 bad_xregs_info_flags(void **bufp, size_t *sizep) 179 { 180 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 181 VERIFY3U(hdr->pr_ninfo, >=, 2); 182 hdr->pr_info[1].pri_flags = 0x120b0; 183 } 184 185 static prxregset_info_t * 186 bad_xregs_find_info(prxregset_hdr_t *hdr, uint32_t type) 187 { 188 for (uint32_t i = 0; i < hdr->pr_ninfo; i++) { 189 if (hdr->pr_info[i].pri_type == type) { 190 return (&hdr->pr_info[i]); 191 } 192 } 193 194 return (NULL); 195 } 196 197 static void 198 bad_xregs_info_xcr_len(void **bufp, size_t *sizep) 199 { 200 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 201 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 202 VERIFY3P(info, !=, NULL); 203 info->pri_size--; 204 } 205 206 static void 207 bad_xregs_info_xcr_off(void **bufp, size_t *sizep) 208 { 209 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 210 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 211 VERIFY3P(info, !=, NULL); 212 info->pri_offset++; 213 } 214 215 static void 216 bad_xregs_info_xsave_len(void **bufp, size_t *sizep) 217 { 218 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 219 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 220 VERIFY3P(info, !=, NULL); 221 info->pri_size--; 222 } 223 224 static void 225 bad_xregs_info_xsave_off(void **bufp, size_t *sizep) 226 { 227 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 228 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 229 VERIFY3P(info, !=, NULL); 230 info->pri_offset--; 231 } 232 233 static void 234 bad_xregs_info_ymm_len(void **bufp, size_t *sizep) 235 { 236 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 237 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_YMM); 238 VERIFY3P(info, !=, NULL); 239 info->pri_size--; 240 } 241 242 static void 243 bad_xregs_info_ymm_off(void **bufp, size_t *sizep) 244 { 245 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 246 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_YMM); 247 VERIFY3P(info, !=, NULL); 248 info->pri_offset--; 249 } 250 251 static void 252 bad_xregs_info_opmask_len(void **bufp, size_t *sizep) 253 { 254 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 255 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_OPMASK); 256 VERIFY3P(info, !=, NULL); 257 info->pri_size--; 258 } 259 260 static void 261 bad_xregs_info_opmask_off(void **bufp, size_t *sizep) 262 { 263 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 264 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_OPMASK); 265 VERIFY3P(info, !=, NULL); 266 info->pri_offset--; 267 } 268 269 static void 270 bad_xregs_info_zmm_len(void **bufp, size_t *sizep) 271 { 272 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 273 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_ZMM); 274 VERIFY3P(info, !=, NULL); 275 info->pri_size--; 276 } 277 278 static void 279 bad_xregs_info_zmm_off(void **bufp, size_t *sizep) 280 { 281 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 282 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_ZMM); 283 VERIFY3P(info, !=, NULL); 284 info->pri_offset--; 285 } 286 287 static void 288 bad_xregs_info_hi_zmm_len(void **bufp, size_t *sizep) 289 { 290 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 291 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_HI_ZMM); 292 VERIFY3P(info, !=, NULL); 293 info->pri_size--; 294 } 295 296 static void 297 bad_xregs_info_hi_zmm_off(void **bufp, size_t *sizep) 298 { 299 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 300 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_HI_ZMM); 301 VERIFY3P(info, !=, NULL); 302 info->pri_offset--; 303 } 304 305 static void 306 bad_xregs_info_exceeds_len0(void **bufp, size_t *sizep) 307 { 308 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 309 hdr->pr_info[0].pri_offset = hdr->pr_size + 4; 310 } 311 312 static void 313 bad_xregs_info_exceeds_len1(void **bufp, size_t *sizep) 314 { 315 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 316 hdr->pr_info[0].pri_offset = hdr->pr_size - hdr->pr_info[0].pri_size + 317 8; 318 } 319 320 static void 321 bad_xregs_info_overlaps(void **bufp, size_t *sizep) 322 { 323 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 324 hdr->pr_info[0].pri_offset = sizeof (prxregset_hdr_t) + 8; 325 } 326 327 static void 328 bad_xregs_trim_entry(prxregset_hdr_t *hdr, uint32_t type) 329 { 330 boolean_t found = B_FALSE; 331 /* 332 * Walk the info structures and clip out everything after the xsave 333 * entry. This almost suggets it'd be nice to have a nop type that was 334 * ignored. 335 */ 336 for (uint32_t i = 0; i < hdr->pr_ninfo; i++) { 337 if (hdr->pr_info[i].pri_type == type) { 338 found = B_TRUE; 339 } 340 341 if (found && i + 1 != hdr->pr_ninfo) { 342 hdr->pr_info[i] = hdr->pr_info[i + 1]; 343 } 344 } 345 346 VERIFY3U(found, ==, B_TRUE); 347 hdr->pr_ninfo--; 348 } 349 350 static void 351 bad_xregs_no_xsave(void **bufp, size_t *sizep) 352 { 353 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 354 bad_xregs_trim_entry(hdr, PRX_INFO_XSAVE); 355 } 356 357 static void 358 bad_xregs_missing_xstate(void **bufp, size_t *sizep) 359 { 360 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 361 bad_xregs_trim_entry(hdr, PRX_INFO_YMM); 362 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 363 VERIFY3P(info, !=, NULL); 364 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 365 info->pri_offset); 366 367 xsave->prx_xsh_xstate_bv |= XFEATURE_AVX; 368 } 369 370 static void 371 bad_xregs_xcr_bad_xcr0(void **bufp, size_t *sizep) 372 { 373 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 374 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 375 VERIFY3P(info, !=, NULL); 376 prxregset_xcr_t *xcr = (void *)((uintptr_t)*bufp + info->pri_offset); 377 xcr->prx_xcr_xcr0 = ~xcr->prx_xcr_xcr0; 378 } 379 380 static void 381 bad_xregs_xcr_bad_xfd(void **bufp, size_t *sizep) 382 { 383 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 384 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 385 VERIFY3P(info, !=, NULL); 386 prxregset_xcr_t *xcr = (void *)((uintptr_t)*bufp + info->pri_offset); 387 xcr->prx_xcr_xfd = ~xcr->prx_xcr_xfd; 388 } 389 390 static void 391 bad_xregs_xcr_bad_pad0(void **bufp, size_t *sizep) 392 { 393 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 394 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 395 VERIFY3P(info, !=, NULL); 396 prxregset_xcr_t *xcr = (void *)((uintptr_t)*bufp + info->pri_offset); 397 xcr->prx_xcr_pad[0] = 0xdeadbeef; 398 } 399 400 static void 401 bad_xregs_xcr_bad_pad1(void **bufp, size_t *sizep) 402 { 403 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 404 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XCR); 405 VERIFY3P(info, !=, NULL); 406 prxregset_xcr_t *xcr = (void *)((uintptr_t)*bufp + info->pri_offset); 407 xcr->prx_xcr_pad[1] = 0xf00b412; 408 } 409 410 static void 411 bad_xregs_xsave_bad_xbv(void **bufp, size_t *sizep) 412 { 413 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 414 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 415 VERIFY3P(info, !=, NULL); 416 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 417 info->pri_offset); 418 /* 419 * bit 8 is a supervisor state that we don't currently have defined in 420 * <sys/x86_archext.h> and should always end up being something we don't 421 * see in userland. 422 */ 423 xsave->prx_xsh_xstate_bv |= (1 << 8); 424 } 425 426 static void 427 bad_xregs_xsave_bad_xcomp(void **bufp, size_t *sizep) 428 { 429 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 430 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 431 VERIFY3P(info, !=, NULL); 432 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 433 info->pri_offset); 434 /* 435 * bit 63 is used to say that this is valid. Given that we don't support 436 * it, we just set that bit as the most realistic example of what could 437 * happen. 438 */ 439 xsave->prx_xsh_xcomp_bv |= (1ULL << 63); 440 } 441 442 static void 443 bad_xregs_xsave_bad_rsvd0(void **bufp, size_t *sizep) 444 { 445 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 446 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 447 VERIFY3P(info, !=, NULL); 448 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 449 info->pri_offset); 450 xsave->prx_xsh_reserved[0] = 0xff10; 451 } 452 453 static void 454 bad_xregs_xsave_bad_rsvd1(void **bufp, size_t *sizep) 455 { 456 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 457 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 458 VERIFY3P(info, !=, NULL); 459 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 460 info->pri_offset); 461 xsave->prx_xsh_reserved[1] = 0x87654321; 462 } 463 464 static void 465 bad_xregs_xsave_bad_rsvd2(void **bufp, size_t *sizep) 466 { 467 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 468 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 469 VERIFY3P(info, !=, NULL); 470 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 471 info->pri_offset); 472 xsave->prx_xsh_reserved[2] = 0x167169; 473 } 474 475 static void 476 bad_xregs_xsave_bad_rsvd3(void **bufp, size_t *sizep) 477 { 478 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 479 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 480 VERIFY3P(info, !=, NULL); 481 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 482 info->pri_offset); 483 xsave->prx_xsh_reserved[3] = 0xff7; 484 } 485 486 static void 487 bad_xregs_xsave_bad_rsvd4(void **bufp, size_t *sizep) 488 { 489 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 490 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 491 VERIFY3P(info, !=, NULL); 492 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 493 info->pri_offset); 494 xsave->prx_xsh_reserved[4] = 0x00f00; 495 } 496 497 static void 498 bad_xregs_xsave_bad_rsvd5(void **bufp, size_t *sizep) 499 { 500 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 501 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_XSAVE); 502 VERIFY3P(info, !=, NULL); 503 prxregset_xsave_t *xsave = (void *)((uintptr_t)*bufp + 504 info->pri_offset); 505 xsave->prx_xsh_reserved[5] = 0x2374013; 506 } 507 508 /* 509 * The following tests are all 32-bit specific. 510 */ 511 #ifdef __i386 512 static void 513 bad_xregs_ymm_ilp32(void **bufp, size_t *sizep) 514 { 515 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 516 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_YMM); 517 VERIFY3P(info, !=, NULL); 518 prxregset_ymm_t *ymm = (void *)((uintptr_t)*bufp + info->pri_offset); 519 ymm->prx_rsvd[4]._l[3] = 0x12345; 520 } 521 522 static void 523 bad_xregs_zmm_ilp32(void **bufp, size_t *sizep) 524 { 525 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 526 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_ZMM); 527 VERIFY3P(info, !=, NULL); 528 prxregset_zmm_t *zmm = (void *)((uintptr_t)*bufp + info->pri_offset); 529 zmm->prx_rsvd[2]._l[5] = 0x23456; 530 } 531 532 static void 533 bad_xregs_hi_zmm_ilp32(void **bufp, size_t *sizep) 534 { 535 prxregset_hdr_t *hdr = bad_xregs_std_init(bufp, sizep); 536 prxregset_info_t *info = bad_xregs_find_info(hdr, PRX_INFO_HI_ZMM); 537 VERIFY3P(info, !=, NULL); 538 prxregset_hi_zmm_t *hi_zmm = (void *)((uintptr_t)*bufp + 539 info->pri_offset); 540 hi_zmm->prx_rsvd[1]._l[9] = 0x34567; 541 } 542 #endif /* __i386 */ 543 544 static const bad_xregs_test_t bad_tests[] = { 545 { "no data (NULL buffer)", EINVAL, XSU_YMM, bad_xregs_no_data }, 546 { "NULL buffer, non-zero count", EFAULT, XSU_YMM, bad_xregs_null_buf }, 547 { "incomplete prxregset_hdr_t", EINVAL, XSU_YMM, bad_xregs_short_hdr }, 548 { "prxregset_hdr_t has wrong type", EINVAL, XSU_YMM, 549 bad_xregs_hdr_bad_type }, 550 { "prxregset_hdr_t size is too large", EINVAL, XSU_YMM, 551 bad_xregs_hdr_too_large }, 552 { "prxregset_hdr_t size bigger than /proc write", EINVAL, XSU_YMM, 553 bad_xregs_missing_data }, 554 { "prxregset_hdr_t invalid flags", EINVAL, XSU_YMM, 555 bad_xregs_hdr_bad_flags }, 556 { "prxregset_hdr_t invalid pad[0]", EINVAL, XSU_YMM, 557 bad_xregs_hdr_bad_pad0 }, 558 { "prxregset_hdr_t invalid pad[1]", EINVAL, XSU_YMM, 559 bad_xregs_hdr_bad_pad1 }, 560 { "prxregset_hdr_t invalid pad[2]", EINVAL, XSU_YMM, 561 bad_xregs_hdr_bad_pad2 }, 562 { "prxregset_hdr_t invalid pad[3]", EINVAL, XSU_YMM, 563 bad_xregs_hdr_bad_pad3 }, 564 { "prxregset_hdr_t no info structures", EINVAL, XSU_YMM, 565 bad_xregs_hdr_no_info }, 566 { "prxregset_hdr_t len doesn't cover info structures", EINVAL, XSU_YMM, 567 bad_xregs_hdr_no_info_len }, 568 { "prxregset_info_t has bad flags", EINVAL, XSU_YMM, 569 bad_xregs_info_flags }, 570 { "prxregset_info_t has bad type", EINVAL, XSU_YMM, 571 bad_xregs_info_type }, 572 { "prxregset_info_t has bad len (XCR)", EINVAL, XSU_YMM, 573 bad_xregs_info_xcr_len }, 574 { "prxregset_info_t has bad align (XCR)", EINVAL, XSU_YMM, 575 bad_xregs_info_xcr_off }, 576 { "prxregset_info_t has bad len (XSAVE)", EINVAL, XSU_YMM, 577 bad_xregs_info_xsave_len }, 578 { "prxregset_info_t has bad align (XSAVE)", EINVAL, XSU_YMM, 579 bad_xregs_info_xsave_off }, 580 { "prxregset_info_t has bad len (YMM)", EINVAL, XSU_YMM, 581 bad_xregs_info_ymm_len }, 582 { "prxregset_info_t has bad align (YMM)", EINVAL, XSU_YMM, 583 bad_xregs_info_ymm_off }, 584 { "prxregset_info_t has bad len (OPMASK)", EINVAL, XSU_ZMM, 585 bad_xregs_info_opmask_len }, 586 { "prxregset_info_t has bad align (OPMASK)", EINVAL, XSU_ZMM, 587 bad_xregs_info_opmask_off }, 588 { "prxregset_info_t has bad len (ZMM)", EINVAL, XSU_ZMM, 589 bad_xregs_info_zmm_len }, 590 { "prxregset_info_t has bad align (ZMM)", EINVAL, XSU_ZMM, 591 bad_xregs_info_zmm_off }, 592 { "prxregset_info_t has bad len (HI ZMM)", EINVAL, XSU_ZMM, 593 bad_xregs_info_hi_zmm_len }, 594 { "prxregset_info_t has bad align (HI ZMM)", EINVAL, XSU_ZMM, 595 bad_xregs_info_hi_zmm_off }, 596 { "prxregset_info_t offset exceeds total len (offset beyond len)", 597 EINVAL, XSU_YMM, bad_xregs_info_exceeds_len0 }, 598 { "prxregset_info_t offset exceeds total len (size+offset beyond len)", 599 EINVAL, XSU_YMM, bad_xregs_info_exceeds_len1 }, 600 { "prxregset_info_t offset overlaps info", EINVAL, XSU_YMM, 601 bad_xregs_info_overlaps }, 602 { "prxregset_t missing xsave struct", EINVAL, XSU_YMM, 603 bad_xregs_no_xsave }, 604 { "prxregset_t missing xstate bit-vector entry", EINVAL, XSU_YMM, 605 bad_xregs_missing_xstate }, 606 { "prxregset_xcr_t modified xcr0", EINVAL, XSU_YMM, 607 bad_xregs_xcr_bad_xcr0 }, 608 { "prxregset_xcr_t modified xfd", EINVAL, XSU_YMM, 609 bad_xregs_xcr_bad_xfd }, 610 { "prxregset_xcr_t modified pad[0]", EINVAL, XSU_YMM, 611 bad_xregs_xcr_bad_pad0 }, 612 { "prxregset_xcr_t modified pad[1]", EINVAL, XSU_YMM, 613 bad_xregs_xcr_bad_pad1 }, 614 { "prxregset_xsave_t illegal xbv comp", EINVAL, XSU_YMM, 615 bad_xregs_xsave_bad_xbv }, 616 { "prxregset_xsave_t illegal compressed comp", EINVAL, XSU_YMM, 617 bad_xregs_xsave_bad_xcomp }, 618 { "prxregset_xsave_t illegal rsvd[0]", EINVAL, XSU_YMM, 619 bad_xregs_xsave_bad_rsvd0 }, 620 { "prxregset_xsave_t illegal rsvd[1]", EINVAL, XSU_YMM, 621 bad_xregs_xsave_bad_rsvd1 }, 622 { "prxregset_xsave_t illegal rsvd[2]", EINVAL, XSU_YMM, 623 bad_xregs_xsave_bad_rsvd2 }, 624 { "prxregset_xsave_t illegal rsvd[3]", EINVAL, XSU_YMM, 625 bad_xregs_xsave_bad_rsvd3 }, 626 { "prxregset_xsave_t illegal rsvd[4]", EINVAL, XSU_YMM, 627 bad_xregs_xsave_bad_rsvd4 }, 628 { "prxregset_xsave_t illegal rsvd[5]", EINVAL, XSU_YMM, 629 bad_xregs_xsave_bad_rsvd5 }, 630 /* 631 * These next sets of tests are specific to 32-bit binaries as they're not 632 * allowed to access a bunch of the additional registers that exist. 633 */ 634 #ifdef __i386 635 { "prxregset_ymm_t has non-zero reserved i386 reg", EINVAL, XSU_YMM, 636 bad_xregs_ymm_ilp32 }, 637 { "prxregset_zmm_t has non-zero reserved i386 reg", EINVAL, XSU_ZMM, 638 bad_xregs_zmm_ilp32 }, 639 { "prxregset_hi_zmm_t has non-zero reserved i386 reg", EINVAL, XSU_ZMM, 640 bad_xregs_hi_zmm_ilp32 }, 641 #endif 642 }; 643 644 int 645 main(void) 646 { 647 int ret; 648 int estatus = EXIT_SUCCESS; 649 struct ps_prochandle *P; 650 struct ps_lwphandle *L; 651 thread_t targ; 652 uint32_t hwsup; 653 uint32_t nskip = 0; 654 655 hwsup = xsu_hwsupport(); 656 P = Pgrab(getpid(), PGRAB_RDONLY, &ret); 657 if (P == NULL) { 658 errx(EXIT_FAILURE, "failed to grab ourself: %s", 659 Pgrab_error(ret)); 660 } 661 662 ret = thr_create(NULL, 0, xsu_sleeper_thread, NULL, THR_DETACHED, 663 &targ); 664 if (ret != 0) { 665 errc(EXIT_FAILURE, ret, "failed to create sleeper thread"); 666 } 667 668 L = Lgrab(P, targ, &ret); 669 if (L == NULL) { 670 errx(EXIT_FAILURE, "failed to grab our sleeper thread: %s", 671 Lgrab_error(ret)); 672 } 673 674 ret = Lstop(L, 0); 675 if (ret != 0) { 676 err(EXIT_FAILURE, "failed to stop the sleeper thread"); 677 } 678 679 if (Lgetxregs(L, &bad_xregs_pxr, &bad_xregs_size) != 0) { 680 err(EXIT_FAILURE, "failed to get basic xregs"); 681 } 682 683 if (bad_xregs_size < sizeof (prxregset_hdr_t)) { 684 errx(EXIT_FAILURE, "found bad regset size: %zu", 685 bad_xregs_size); 686 } 687 688 for (size_t i = 0; i < ARRAY_SIZE(bad_tests); i++) { 689 void *buf = NULL; 690 size_t len = 0; 691 692 if (bad_tests[i].bxt_min > hwsup) { 693 warnx("TEST SKIPPED: %s: requires greater hwsup than " 694 "supported (0x%x)", bad_tests[i].bxt_desc, 695 bad_tests[i].bxt_min); 696 nskip++; 697 continue; 698 } 699 700 bad_tests[i].bxt_setup(&buf, &len); 701 if (Lsetxregs(L, buf, len) != -1) { 702 warnx("TEST FAILED: %s: Lsetxregs returned 0, not -1!", 703 bad_tests[i].bxt_desc); 704 estatus = EXIT_FAILURE; 705 } else if (errno != bad_tests[i].bxt_errno) { 706 warnx("TEST FAILED: %s: Lsetxregs errno was %d, " 707 "expected %d", bad_tests[i].bxt_desc, errno, 708 bad_tests[i].bxt_errno); 709 estatus = EXIT_FAILURE; 710 } else { 711 (void) printf("TEST PASSED: %s\n", 712 bad_tests[i].bxt_desc); 713 } 714 free(buf); 715 } 716 717 if (estatus == EXIT_SUCCESS && nskip > 0) { 718 warnx("While tests were successful, %u tests were skipped " 719 "due to missing hardware support", nskip); 720 } 721 722 exit(estatus); 723 } 724