1ed093b41SRobert Mustacchi /*
2ed093b41SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3ed093b41SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4ed093b41SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5ed093b41SRobert Mustacchi  * 1.0 of the CDDL.
6ed093b41SRobert Mustacchi  *
7ed093b41SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8ed093b41SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9ed093b41SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10ed093b41SRobert Mustacchi  */
11ed093b41SRobert Mustacchi 
12ed093b41SRobert Mustacchi /*
13ed093b41SRobert Mustacchi  * Copyright 2023 Oxide Computer Company
14ed093b41SRobert Mustacchi  */
15ed093b41SRobert Mustacchi 
16ed093b41SRobert Mustacchi /*
17ed093b41SRobert Mustacchi  * The purpose of this test is to go through and construct ucontext_t xsave
18ed093b41SRobert Mustacchi  * states that we expect to be invalid and therefore cause setcontext(2) to
19ed093b41SRobert Mustacchi  * fail. We only assume that %ymm state is present here with respect to writing
20ed093b41SRobert Mustacchi  * invalid tests. As if this test runs at all, that will be present.
21ed093b41SRobert Mustacchi  *
22ed093b41SRobert Mustacchi  * This is structured a little differently as we expect this program to fail to
23ed093b41SRobert Mustacchi  * execute and that libc will cause us to abort() if we don't correctly return
24ed093b41SRobert Mustacchi  * from setcontext.
25ed093b41SRobert Mustacchi  */
26ed093b41SRobert Mustacchi 
27ed093b41SRobert Mustacchi #include <ucontext.h>
28ed093b41SRobert Mustacchi #include <err.h>
29ed093b41SRobert Mustacchi #include <stdlib.h>
30ed093b41SRobert Mustacchi #include <strings.h>
31ed093b41SRobert Mustacchi #include <errno.h>
32ed093b41SRobert Mustacchi #include <sys/sysmacros.h>
33ed093b41SRobert Mustacchi #include <sys/debug.h>
34ed093b41SRobert Mustacchi #include <sys/x86_archext.h>
35ed093b41SRobert Mustacchi #include <sys/mman.h>
36ed093b41SRobert Mustacchi #include <unistd.h>
37ed093b41SRobert Mustacchi 
38ed093b41SRobert Mustacchi #include "xsave_util.h"
39ed093b41SRobert Mustacchi 
40ed093b41SRobert Mustacchi static void *xsave_buf;
41*1e56f352SRobert Mustacchi static volatile const char *curtest;
42ed093b41SRobert Mustacchi 
43ed093b41SRobert Mustacchi static void
bad_success(void)44ed093b41SRobert Mustacchi bad_success(void)
45ed093b41SRobert Mustacchi {
46ed093b41SRobert Mustacchi 	errx(EXIT_FAILURE, "TEST FAILED: %s setcontext, took us to success",
47ed093b41SRobert Mustacchi 	    curtest);
48ed093b41SRobert Mustacchi }
49ed093b41SRobert Mustacchi 
50ed093b41SRobert Mustacchi static void
test_bad_version(ucontext_t * ctx)51ed093b41SRobert Mustacchi test_bad_version(ucontext_t *ctx)
52ed093b41SRobert Mustacchi {
53ed093b41SRobert Mustacchi 	uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
54ed093b41SRobert Mustacchi 	xc->ucx_vers = 23;
55ed093b41SRobert Mustacchi }
56ed093b41SRobert Mustacchi 
57ed093b41SRobert Mustacchi static void
test_bad_length_small(ucontext_t * ctx)58ed093b41SRobert Mustacchi test_bad_length_small(ucontext_t *ctx)
59ed093b41SRobert Mustacchi {
60ed093b41SRobert Mustacchi 	uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
61ed093b41SRobert Mustacchi 	xc->ucx_len = 0;
62ed093b41SRobert Mustacchi }
63ed093b41SRobert Mustacchi 
64ed093b41SRobert Mustacchi static void
test_bad_length_large(ucontext_t * ctx)65ed093b41SRobert Mustacchi test_bad_length_large(ucontext_t *ctx)
66ed093b41SRobert Mustacchi {
67ed093b41SRobert Mustacchi 	uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
68ed093b41SRobert Mustacchi 	xc->ucx_len = INT32_MAX;
69ed093b41SRobert Mustacchi }
70ed093b41SRobert Mustacchi 
71ed093b41SRobert Mustacchi /*
72ed093b41SRobert Mustacchi  * As this can run on multiple different systems, we explicitly use bit 8 which
73ed093b41SRobert Mustacchi  * is reserved for a supervisor feature and so should never be valid in this
74ed093b41SRobert Mustacchi  * context.
75ed093b41SRobert Mustacchi  */
76ed093b41SRobert Mustacchi static void
test_bad_vector(ucontext_t * ctx)77ed093b41SRobert Mustacchi test_bad_vector(ucontext_t *ctx)
78ed093b41SRobert Mustacchi {
79ed093b41SRobert Mustacchi 	uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
80ed093b41SRobert Mustacchi 	xc->ucx_bv |= (1 << 8);
81ed093b41SRobert Mustacchi }
82ed093b41SRobert Mustacchi 
83ed093b41SRobert Mustacchi static void
test_context_too_short(ucontext_t * ctx)84ed093b41SRobert Mustacchi test_context_too_short(ucontext_t *ctx)
85ed093b41SRobert Mustacchi {
86ed093b41SRobert Mustacchi 	uc_xsave_t *xc = (uc_xsave_t *)ctx->uc_xsave;
87ed093b41SRobert Mustacchi 
88ed093b41SRobert Mustacchi 	bcopy(xc, xsave_buf, xc->ucx_len);
89ed093b41SRobert Mustacchi 	ctx->uc_xsave = (long)(uintptr_t)xsave_buf;
90ed093b41SRobert Mustacchi 	xc = (uc_xsave_t *)ctx->uc_xsave;
91ed093b41SRobert Mustacchi 	xc->ucx_bv |= XFEATURE_AVX;
92ed093b41SRobert Mustacchi 	xc->ucx_len = sizeof (uc_xsave_t) + 0x10;
93ed093b41SRobert Mustacchi }
94ed093b41SRobert Mustacchi 
95ed093b41SRobert Mustacchi static void
test_context_badptr0(ucontext_t * ctx)96ed093b41SRobert Mustacchi test_context_badptr0(ucontext_t *ctx)
97ed093b41SRobert Mustacchi {
98ed093b41SRobert Mustacchi 	ctx->uc_xsave = 0;
99ed093b41SRobert Mustacchi }
100ed093b41SRobert Mustacchi 
101ed093b41SRobert Mustacchi static void
test_context_badptr1(ucontext_t * ctx)102ed093b41SRobert Mustacchi test_context_badptr1(ucontext_t *ctx)
103ed093b41SRobert Mustacchi {
104ed093b41SRobert Mustacchi 	void *addr = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_NONE,
105ed093b41SRobert Mustacchi 	    MAP_PRIVATE | MAP_ANON, -1, 0);
106ed093b41SRobert Mustacchi 	if (addr == NULL) {
107ed093b41SRobert Mustacchi 		err(EXIT_FAILURE, "failed to get unmapped page");
108ed093b41SRobert Mustacchi 	}
109ed093b41SRobert Mustacchi 
110ed093b41SRobert Mustacchi 	ctx->uc_xsave = (long)(uintptr_t)addr;
111ed093b41SRobert Mustacchi }
112ed093b41SRobert Mustacchi 
113ed093b41SRobert Mustacchi static void
test_context_badptr2(ucontext_t * ctx)114ed093b41SRobert Mustacchi test_context_badptr2(ucontext_t *ctx)
115ed093b41SRobert Mustacchi {
116ed093b41SRobert Mustacchi 	long pgsz = sysconf(_SC_PAGESIZE);
117ed093b41SRobert Mustacchi 	void *addr = mmap(NULL, pgsz * 2, PROT_NONE, MAP_PRIVATE | MAP_ANON,
118ed093b41SRobert Mustacchi 	    -1, 0);
119ed093b41SRobert Mustacchi 	if (addr == NULL) {
120ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to get unmapped page");
121ed093b41SRobert Mustacchi 	}
122ed093b41SRobert Mustacchi 
123ed093b41SRobert Mustacchi 	if (mprotect((void *)((uintptr_t)addr + pgsz), pgsz, PROT_NONE) != 0) {
124ed093b41SRobert Mustacchi 		err(EXIT_FAILURE, "failed to mprotect second page");
125ed093b41SRobert Mustacchi 	}
126ed093b41SRobert Mustacchi 
127ed093b41SRobert Mustacchi 	ctx->uc_xsave = (uintptr_t)addr;
128ed093b41SRobert Mustacchi 	ctx->uc_xsave += pgsz - sizeof (uint64_t);
129ed093b41SRobert Mustacchi }
130ed093b41SRobert Mustacchi 
131ed093b41SRobert Mustacchi static ucontext_t *
setup_context(void)132ed093b41SRobert Mustacchi setup_context(void)
133ed093b41SRobert Mustacchi {
134ed093b41SRobert Mustacchi 	ucontext_t *ctx = ucontext_alloc(0);
135ed093b41SRobert Mustacchi 	if (ctx == NULL) {
136ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to get allocate ucontext_t");
137ed093b41SRobert Mustacchi 	}
138ed093b41SRobert Mustacchi 
139ed093b41SRobert Mustacchi 	if (getcontext_extd(ctx, 0) != 0) {
140ed093b41SRobert Mustacchi 		err(EXIT_FAILURE, "failed to get extended context");
141ed093b41SRobert Mustacchi 	}
142ed093b41SRobert Mustacchi 	xsu_ustack_alloc(ctx);
143ed093b41SRobert Mustacchi 	makecontext(ctx, bad_success, 0);
144ed093b41SRobert Mustacchi 
145ed093b41SRobert Mustacchi 	return (ctx);
146ed093b41SRobert Mustacchi }
147ed093b41SRobert Mustacchi 
148ed093b41SRobert Mustacchi typedef struct {
149ed093b41SRobert Mustacchi 	void (*bct_func)(ucontext_t *);
150ed093b41SRobert Mustacchi 	const char *bct_test;
151ed093b41SRobert Mustacchi 	int bct_errno;
152ed093b41SRobert Mustacchi } bad_ucontext_test_t;
153ed093b41SRobert Mustacchi 
154ed093b41SRobert Mustacchi /*
155ed093b41SRobert Mustacchi  * Do not use single quote characters in tests below, that'll break the shell
156ed093b41SRobert Mustacchi  * wrapper.
157ed093b41SRobert Mustacchi  */
158ed093b41SRobert Mustacchi static const bad_ucontext_test_t tests[] = {
159ed093b41SRobert Mustacchi 	{ test_bad_version, "invalid version", EINVAL },
160ed093b41SRobert Mustacchi 	{ test_bad_length_small, "invalid length (small)", EINVAL },
161ed093b41SRobert Mustacchi 	{ test_bad_length_large, "invalid length (large)", EINVAL },
162ed093b41SRobert Mustacchi 	{ test_bad_vector, "invalid xbv", EINVAL },
163ed093b41SRobert Mustacchi 	{ test_context_too_short, "length does not cover AVX", EOVERFLOW },
164ed093b41SRobert Mustacchi 	{ test_context_badptr0, "invalid uc_xsave pointer (NULL)", EINVAL },
165ed093b41SRobert Mustacchi 	{ test_context_badptr1, "invalid uc_xsave pointer (unmapped page)",
166ed093b41SRobert Mustacchi 	    EFAULT },
167ed093b41SRobert Mustacchi 	{ test_context_badptr2, "partially invalid uc_xsave (hit "
168ed093b41SRobert Mustacchi 	    "unmapped page)", EFAULT },
169ed093b41SRobert Mustacchi };
170ed093b41SRobert Mustacchi 
171ed093b41SRobert Mustacchi int
main(int argc,char * argv[])172ed093b41SRobert Mustacchi main(int argc, char *argv[])
173ed093b41SRobert Mustacchi {
174ed093b41SRobert Mustacchi 	int c;
175ed093b41SRobert Mustacchi 	char *eptr;
176ed093b41SRobert Mustacchi 	unsigned long l;
177ed093b41SRobert Mustacchi 	const char *testno = NULL;
178ed093b41SRobert Mustacchi 	boolean_t do_info = B_FALSE, do_run = B_FALSE;
179ed093b41SRobert Mustacchi 
180ed093b41SRobert Mustacchi 	if (argc < 2) {
181ed093b41SRobert Mustacchi 		(void) fprintf(stderr, "Usage:  %s [-c] [-i testno] "
182ed093b41SRobert Mustacchi 		    "[-r testno]\n", argv[0]);
183ed093b41SRobert Mustacchi 	}
184ed093b41SRobert Mustacchi 
185ed093b41SRobert Mustacchi 	while ((c = getopt(argc, argv, ":ci:r:")) != -1) {
186ed093b41SRobert Mustacchi 		switch (c) {
187ed093b41SRobert Mustacchi 		case 'c':
188ed093b41SRobert Mustacchi 			(void) printf("%zu\n", ARRAY_SIZE(tests));
189ed093b41SRobert Mustacchi 			return (0);
190ed093b41SRobert Mustacchi 		case 'i':
191ed093b41SRobert Mustacchi 			testno = optarg;
192ed093b41SRobert Mustacchi 			do_info = B_TRUE;
193ed093b41SRobert Mustacchi 			break;
194ed093b41SRobert Mustacchi 		case 'r':
195ed093b41SRobert Mustacchi 			testno = optarg;
196ed093b41SRobert Mustacchi 			do_run = B_TRUE;
197ed093b41SRobert Mustacchi 			break;
198ed093b41SRobert Mustacchi 		case ':':
199ed093b41SRobert Mustacchi 			errx(EXIT_FAILURE, "Option -%c requires an operand\n",
200ed093b41SRobert Mustacchi 			    optopt);
201ed093b41SRobert Mustacchi 			break;
202ed093b41SRobert Mustacchi 		case '?':
203ed093b41SRobert Mustacchi 			errx(EXIT_FAILURE, "Unknown option: -%c\n", optopt);
204ed093b41SRobert Mustacchi 			break;
205ed093b41SRobert Mustacchi 		}
206ed093b41SRobert Mustacchi 	}
207ed093b41SRobert Mustacchi 
208ed093b41SRobert Mustacchi 	if (testno == NULL) {
209ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "one of -r and -i must be specified");
210ed093b41SRobert Mustacchi 	}
211ed093b41SRobert Mustacchi 
212ed093b41SRobert Mustacchi 	if (do_run && do_info) {
213ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "only one of -r and -i may be specified");
214ed093b41SRobert Mustacchi 	}
215ed093b41SRobert Mustacchi 
216ed093b41SRobert Mustacchi 	errno = 0;
217ed093b41SRobert Mustacchi 	l = strtoul(testno, &eptr, 0);
218ed093b41SRobert Mustacchi 	if (*eptr != 0 || errno != 0) {
219ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "failed to parse test number: %s", argv[1]);
220ed093b41SRobert Mustacchi 	}
221ed093b41SRobert Mustacchi 
222ed093b41SRobert Mustacchi 	if (l >= ARRAY_SIZE(tests)) {
223ed093b41SRobert Mustacchi 		errx(EXIT_FAILURE, "test number %lu is too large\n", l);
224ed093b41SRobert Mustacchi 	}
225ed093b41SRobert Mustacchi 
226ed093b41SRobert Mustacchi 	if (do_info) {
227ed093b41SRobert Mustacchi 		/*
228ed093b41SRobert Mustacchi 		 * Output info for our wrapper shell script in a way that's not
229ed093b41SRobert Mustacchi 		 * too bad to eval.
230ed093b41SRobert Mustacchi 		 */
231ed093b41SRobert Mustacchi 		(void) printf("errno=%u\ndesc='%s'\n", tests[l].bct_errno,
232ed093b41SRobert Mustacchi 		    tests[l].bct_test);
233ed093b41SRobert Mustacchi 		return (0);
234ed093b41SRobert Mustacchi 	}
235ed093b41SRobert Mustacchi 
236ed093b41SRobert Mustacchi 	/*
237ed093b41SRobert Mustacchi 	 * This is a little gross, but we know right now that the extended
238ed093b41SRobert Mustacchi 	 * context is going to be the approximate size that we need for
239ed093b41SRobert Mustacchi 	 * operations on the system.
240ed093b41SRobert Mustacchi 	 */
241ed093b41SRobert Mustacchi 	xsave_buf = ucontext_alloc(0);
242ed093b41SRobert Mustacchi 	if (xsave_buf == NULL) {
243ed093b41SRobert Mustacchi 		err(EXIT_FAILURE, "failed to alternative xsave buf");
244ed093b41SRobert Mustacchi 	}
245ed093b41SRobert Mustacchi 
246ed093b41SRobert Mustacchi 	ucontext_t *ctx = setup_context();
247ed093b41SRobert Mustacchi 	VERIFY3U(ctx->uc_xsave, !=, 0);
248*1e56f352SRobert Mustacchi 	curtest = tests[l].bct_test;
249ed093b41SRobert Mustacchi 	tests[l].bct_func(ctx);
250ed093b41SRobert Mustacchi 	(void) setcontext(ctx);
251ed093b41SRobert Mustacchi 	errx(EXIT_FAILURE, "TEST FAILED: setcontext returned despite us "
252ed093b41SRobert Mustacchi 	    "expecting a core");
253ed093b41SRobert Mustacchi }
254