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
bad_xregs_no_data(void ** bufp,size_t * sizep)46 bad_xregs_no_data(void **bufp, size_t *sizep)
47 {
48 *bufp = NULL;
49 *sizep = 0;
50 }
51
52 static void
bad_xregs_null_buf(void ** bufp,size_t * sizep)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
bad_xregs_short_hdr(void ** bufp,size_t * sizep)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
bad_xregs_hdr_too_large(void ** bufp,size_t * sizep)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 *
bad_xregs_std_init(void ** bufp,size_t * sizep)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
bad_xregs_missing_data(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_type(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_flags(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_pad0(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_pad1(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_pad2(void ** bufp,size_t * sizep)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
bad_xregs_hdr_bad_pad3(void ** bufp,size_t * sizep)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
bad_xregs_hdr_no_info(void ** bufp,size_t * sizep)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
bad_xregs_hdr_no_info_len(void ** bufp,size_t * sizep)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
bad_xregs_info_type(void ** bufp,size_t * sizep)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
bad_xregs_info_flags(void ** bufp,size_t * sizep)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 *
bad_xregs_find_info(prxregset_hdr_t * hdr,uint32_t type)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
bad_xregs_info_xcr_len(void ** bufp,size_t * sizep)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
bad_xregs_info_xcr_off(void ** bufp,size_t * sizep)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
bad_xregs_info_xsave_len(void ** bufp,size_t * sizep)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
bad_xregs_info_xsave_off(void ** bufp,size_t * sizep)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
bad_xregs_info_ymm_len(void ** bufp,size_t * sizep)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
bad_xregs_info_ymm_off(void ** bufp,size_t * sizep)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
bad_xregs_info_opmask_len(void ** bufp,size_t * sizep)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
bad_xregs_info_opmask_off(void ** bufp,size_t * sizep)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
bad_xregs_info_zmm_len(void ** bufp,size_t * sizep)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
bad_xregs_info_zmm_off(void ** bufp,size_t * sizep)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
bad_xregs_info_hi_zmm_len(void ** bufp,size_t * sizep)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
bad_xregs_info_hi_zmm_off(void ** bufp,size_t * sizep)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
bad_xregs_info_exceeds_len0(void ** bufp,size_t * sizep)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
bad_xregs_info_exceeds_len1(void ** bufp,size_t * sizep)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
bad_xregs_info_overlaps(void ** bufp,size_t * sizep)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
bad_xregs_trim_entry(prxregset_hdr_t * hdr,uint32_t type)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
bad_xregs_no_xsave(void ** bufp,size_t * sizep)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
bad_xregs_missing_xstate(void ** bufp,size_t * sizep)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
bad_xregs_xcr_bad_xcr0(void ** bufp,size_t * sizep)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
bad_xregs_xcr_bad_xfd(void ** bufp,size_t * sizep)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
bad_xregs_xcr_bad_pad0(void ** bufp,size_t * sizep)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
bad_xregs_xcr_bad_pad1(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_xbv(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_xcomp(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd0(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd1(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd2(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd3(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd4(void ** bufp,size_t * sizep)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
bad_xregs_xsave_bad_rsvd5(void ** bufp,size_t * sizep)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
bad_xregs_ymm_ilp32(void ** bufp,size_t * sizep)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
bad_xregs_zmm_ilp32(void ** bufp,size_t * sizep)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
bad_xregs_hi_zmm_ilp32(void ** bufp,size_t * sizep)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
main(void)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