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