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 }