xref: /illumos-gate/usr/src/test/os-tests/tests/spawn/spawn-syscall.c (revision ac2250cb76bb32944fd2c8a3ba2cd3f79747748d)
1*ac2250cbSAndy Fiddaman /*
2*ac2250cbSAndy Fiddaman  * This file and its contents are supplied under the terms of the
3*ac2250cbSAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4*ac2250cbSAndy Fiddaman  * You may only use this file in accordance with the terms of version
5*ac2250cbSAndy Fiddaman  * 1.0 of the CDDL.
6*ac2250cbSAndy Fiddaman  *
7*ac2250cbSAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8*ac2250cbSAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9*ac2250cbSAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10*ac2250cbSAndy Fiddaman  */
11*ac2250cbSAndy Fiddaman 
12*ac2250cbSAndy Fiddaman /*
13*ac2250cbSAndy Fiddaman  * Copyright 2026 Oxide Computer Company
14*ac2250cbSAndy Fiddaman  */
15*ac2250cbSAndy Fiddaman 
16*ac2250cbSAndy Fiddaman /*
17*ac2250cbSAndy Fiddaman  * Tests for the raw spawn(2) system call interface. These concentrate
18*ac2250cbSAndy Fiddaman  * mainly on checking that the kernel fully validates the marshalled
19*ac2250cbSAndy Fiddaman  * spawn_param_t/spawn_args_t structures that libc constructs.
20*ac2250cbSAndy Fiddaman  * We also test that a spawned child is robust against being killed in
21*ac2250cbSAndy Fiddaman  * the window between creation and exec.
22*ac2250cbSAndy Fiddaman  *
23*ac2250cbSAndy Fiddaman  * Functional coverage of posix_spawn(3C) itself (attributes, file actions,
24*ac2250cbSAndy Fiddaman  * PATH handling, etc.) lives in libc-tests/tests/posix_spawn.
25*ac2250cbSAndy Fiddaman  */
26*ac2250cbSAndy Fiddaman 
27*ac2250cbSAndy Fiddaman #include <errno.h>
28*ac2250cbSAndy Fiddaman #include <signal.h>
29*ac2250cbSAndy Fiddaman #include <spawn.h>
30*ac2250cbSAndy Fiddaman #include <stdio.h>
31*ac2250cbSAndy Fiddaman #include <stdlib.h>
32*ac2250cbSAndy Fiddaman #include <string.h>
33*ac2250cbSAndy Fiddaman #include <unistd.h>
34*ac2250cbSAndy Fiddaman #include <sys/syscall.h>
35*ac2250cbSAndy Fiddaman #include <sys/spawn_impl.h>
36*ac2250cbSAndy Fiddaman #include <sys/types.h>
37*ac2250cbSAndy Fiddaman #include <sys/wait.h>
38*ac2250cbSAndy Fiddaman 
39*ac2250cbSAndy Fiddaman static uint_t failures = 0;
40*ac2250cbSAndy Fiddaman 
41*ac2250cbSAndy Fiddaman #define	TFAIL(name, fmt, ...)	do {					\
42*ac2250cbSAndy Fiddaman 	(void) fprintf(stderr, "TEST FAILED: %s: " fmt "\n",		\
43*ac2250cbSAndy Fiddaman 	    (name), ##__VA_ARGS__);					\
44*ac2250cbSAndy Fiddaman 	failures++;							\
45*ac2250cbSAndy Fiddaman } while (0)
46*ac2250cbSAndy Fiddaman 
47*ac2250cbSAndy Fiddaman #define	TPASS(name)	(void) printf("TEST PASSED: %s\n", (name))
48*ac2250cbSAndy Fiddaman 
49*ac2250cbSAndy Fiddaman /*
50*ac2250cbSAndy Fiddaman  * The spawn_args_t and spawn_param_t structures end in a variable-length
51*ac2250cbSAndy Fiddaman  * data[] region. These tests build small instances on the stack and reserve a
52*ac2250cbSAndy Fiddaman  * fixed amount of room for that region.
53*ac2250cbSAndy Fiddaman  */
54*ac2250cbSAndy Fiddaman #define	SA_DATA	16
55*ac2250cbSAndy Fiddaman #define	SP_DATA	64
56*ac2250cbSAndy Fiddaman 
57*ac2250cbSAndy Fiddaman static pid_t
raw_spawn(const char * path,const void * sp,uint32_t spsize,const void * sa,uint32_t sasize)58*ac2250cbSAndy Fiddaman raw_spawn(const char *path, const void *sp, uint32_t spsize, const void *sa,
59*ac2250cbSAndy Fiddaman     uint32_t sasize)
60*ac2250cbSAndy Fiddaman {
61*ac2250cbSAndy Fiddaman 	return (syscall(SYS_spawn, path, sp, spsize, sa, sasize));
62*ac2250cbSAndy Fiddaman }
63*ac2250cbSAndy Fiddaman 
64*ac2250cbSAndy Fiddaman /*
65*ac2250cbSAndy Fiddaman  * Build a minimal valid spawn_args_t in the provided buffer:
66*ac2250cbSAndy Fiddaman  * argv = { "x" }, empty environment.
67*ac2250cbSAndy Fiddaman  */
68*ac2250cbSAndy Fiddaman static uint32_t
valid_args(spawn_args_t * sa)69*ac2250cbSAndy Fiddaman valid_args(spawn_args_t *sa)
70*ac2250cbSAndy Fiddaman {
71*ac2250cbSAndy Fiddaman 	uint32_t sasize = sizeof (*sa) + 2;
72*ac2250cbSAndy Fiddaman 
73*ac2250cbSAndy Fiddaman 	(void) memset(sa, 0, sasize);
74*ac2250cbSAndy Fiddaman 	sa->sa_size = sasize;
75*ac2250cbSAndy Fiddaman 	sa->sa_datalen = 2;
76*ac2250cbSAndy Fiddaman 	sa->sa_arg_cnt = 1;
77*ac2250cbSAndy Fiddaman 	sa->sa_env_off = 2;
78*ac2250cbSAndy Fiddaman 	sa->sa_data[0] = 'x';
79*ac2250cbSAndy Fiddaman 	sa->sa_data[1] = '\0';
80*ac2250cbSAndy Fiddaman 
81*ac2250cbSAndy Fiddaman 	return (sasize);
82*ac2250cbSAndy Fiddaman }
83*ac2250cbSAndy Fiddaman 
84*ac2250cbSAndy Fiddaman static const char *
errname(int err)85*ac2250cbSAndy Fiddaman errname(int err)
86*ac2250cbSAndy Fiddaman {
87*ac2250cbSAndy Fiddaman 	const char *name = strerrorname_np(err);
88*ac2250cbSAndy Fiddaman 
89*ac2250cbSAndy Fiddaman 	return (name != NULL ? name : "?");
90*ac2250cbSAndy Fiddaman }
91*ac2250cbSAndy Fiddaman 
92*ac2250cbSAndy Fiddaman static void
expect_err(const char * name,int wanted,const char * path,const void * sp,uint32_t spsize,const void * sa,uint32_t sasize)93*ac2250cbSAndy Fiddaman expect_err(const char *name, int wanted, const char *path, const void *sp,
94*ac2250cbSAndy Fiddaman     uint32_t spsize, const void *sa, uint32_t sasize)
95*ac2250cbSAndy Fiddaman {
96*ac2250cbSAndy Fiddaman 	pid_t pid = raw_spawn(path, sp, spsize, sa, sasize);
97*ac2250cbSAndy Fiddaman 
98*ac2250cbSAndy Fiddaman 	if (pid != -1) {
99*ac2250cbSAndy Fiddaman 		TFAIL(name, "spawn unexpectedly succeeded (pid %d)",
100*ac2250cbSAndy Fiddaman 		    (int)pid);
101*ac2250cbSAndy Fiddaman 		(void) waitpid(pid, NULL, 0);
102*ac2250cbSAndy Fiddaman 		return;
103*ac2250cbSAndy Fiddaman 	}
104*ac2250cbSAndy Fiddaman 	if (errno != wanted) {
105*ac2250cbSAndy Fiddaman 		TFAIL(name, "got %s (%d), wanted %s (%d)", errname(errno),
106*ac2250cbSAndy Fiddaman 		    errno, errname(wanted), wanted);
107*ac2250cbSAndy Fiddaman 		return;
108*ac2250cbSAndy Fiddaman 	}
109*ac2250cbSAndy Fiddaman 	TPASS(name);
110*ac2250cbSAndy Fiddaman }
111*ac2250cbSAndy Fiddaman 
112*ac2250cbSAndy Fiddaman /*
113*ac2250cbSAndy Fiddaman  * The positive case: a well-formed minimal request - a valid argument vector,
114*ac2250cbSAndy Fiddaman  * no spawn parameters and no file actions - succeeds, and the child
115*ac2250cbSAndy Fiddaman  * (/usr/bin/true) exits 0.
116*ac2250cbSAndy Fiddaman  */
117*ac2250cbSAndy Fiddaman static void
t_valid(void)118*ac2250cbSAndy Fiddaman t_valid(void)
119*ac2250cbSAndy Fiddaman {
120*ac2250cbSAndy Fiddaman 	union {
121*ac2250cbSAndy Fiddaman 		spawn_args_t sa;
122*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_args_t) + SA_DATA];
123*ac2250cbSAndy Fiddaman 	} au;
124*ac2250cbSAndy Fiddaman 	uint32_t sasize = valid_args(&au.sa);
125*ac2250cbSAndy Fiddaman 	int status;
126*ac2250cbSAndy Fiddaman 	pid_t pid;
127*ac2250cbSAndy Fiddaman 
128*ac2250cbSAndy Fiddaman 	pid = raw_spawn("/usr/bin/true", NULL, 0, &au.sa, sasize);
129*ac2250cbSAndy Fiddaman 	if (pid == -1) {
130*ac2250cbSAndy Fiddaman 		TFAIL("valid", "spawn failed: %s", strerror(errno));
131*ac2250cbSAndy Fiddaman 		return;
132*ac2250cbSAndy Fiddaman 	}
133*ac2250cbSAndy Fiddaman 	if (waitpid(pid, &status, 0) != pid) {
134*ac2250cbSAndy Fiddaman 		TFAIL("valid", "waitpid: %s", strerror(errno));
135*ac2250cbSAndy Fiddaman 		return;
136*ac2250cbSAndy Fiddaman 	}
137*ac2250cbSAndy Fiddaman 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
138*ac2250cbSAndy Fiddaman 		TFAIL("valid", "status %#x", status);
139*ac2250cbSAndy Fiddaman 		return;
140*ac2250cbSAndy Fiddaman 	}
141*ac2250cbSAndy Fiddaman 	TPASS("valid, well-formed, minimal spawn request");
142*ac2250cbSAndy Fiddaman }
143*ac2250cbSAndy Fiddaman 
144*ac2250cbSAndy Fiddaman static void
t_args_fuzz(void)145*ac2250cbSAndy Fiddaman t_args_fuzz(void)
146*ac2250cbSAndy Fiddaman {
147*ac2250cbSAndy Fiddaman 	union {
148*ac2250cbSAndy Fiddaman 		spawn_args_t sa;
149*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_args_t) + SA_DATA];
150*ac2250cbSAndy Fiddaman 	} au;
151*ac2250cbSAndy Fiddaman 	spawn_args_t *sa = &au.sa;
152*ac2250cbSAndy Fiddaman 	uint32_t sasize;
153*ac2250cbSAndy Fiddaman 
154*ac2250cbSAndy Fiddaman 	expect_err("null-path", EINVAL, NULL, NULL, 0, &au.sa,
155*ac2250cbSAndy Fiddaman 	    valid_args(&au.sa));
156*ac2250cbSAndy Fiddaman 	expect_err("null-args", EINVAL, "/usr/bin/true", NULL, 0, NULL, 0);
157*ac2250cbSAndy Fiddaman 
158*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
159*ac2250cbSAndy Fiddaman 	sa->sa_size = sasize + 8;
160*ac2250cbSAndy Fiddaman 	expect_err("args-size-mismatch", EINVAL, "/usr/bin/true", NULL, 0,
161*ac2250cbSAndy Fiddaman 	    sa, sasize);
162*ac2250cbSAndy Fiddaman 
163*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
164*ac2250cbSAndy Fiddaman 	sa->sa_datalen = 1;
165*ac2250cbSAndy Fiddaman 	expect_err("args-datalen-mismatch", EINVAL, "/usr/bin/true", NULL, 0,
166*ac2250cbSAndy Fiddaman 	    sa, sasize);
167*ac2250cbSAndy Fiddaman 
168*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
169*ac2250cbSAndy Fiddaman 	sa->sa_arg_cnt = 100;
170*ac2250cbSAndy Fiddaman 	expect_err("args-cnt-overrun", EINVAL, "/usr/bin/true", NULL, 0,
171*ac2250cbSAndy Fiddaman 	    sa, sasize);
172*ac2250cbSAndy Fiddaman 
173*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
174*ac2250cbSAndy Fiddaman 	sa->sa_data[1] = 'y';	/* string no longer NUL-terminated */
175*ac2250cbSAndy Fiddaman 	expect_err("args-unterminated", EINVAL, "/usr/bin/true", NULL, 0,
176*ac2250cbSAndy Fiddaman 	    sa, sasize);
177*ac2250cbSAndy Fiddaman 
178*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
179*ac2250cbSAndy Fiddaman 	sa->sa_env_off = 100;
180*ac2250cbSAndy Fiddaman 	expect_err("args-env-off", EINVAL, "/usr/bin/true", NULL, 0,
181*ac2250cbSAndy Fiddaman 	    sa, sasize);
182*ac2250cbSAndy Fiddaman 
183*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
184*ac2250cbSAndy Fiddaman 	sa->sa_env_cnt = 7;
185*ac2250cbSAndy Fiddaman 	expect_err("args-env-cnt", EINVAL, "/usr/bin/true", NULL, 0,
186*ac2250cbSAndy Fiddaman 	    sa, sasize);
187*ac2250cbSAndy Fiddaman 
188*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
189*ac2250cbSAndy Fiddaman 	expect_err("args-e2big", E2BIG, "/usr/bin/true", NULL, 0,
190*ac2250cbSAndy Fiddaman 	    sa, 0x400000);
191*ac2250cbSAndy Fiddaman 
192*ac2250cbSAndy Fiddaman 	/* Truncated buffer: shorter than the header itself. */
193*ac2250cbSAndy Fiddaman 	sasize = valid_args(sa);
194*ac2250cbSAndy Fiddaman 	expect_err("args-short", EINVAL, "/usr/bin/true", NULL, 0,
195*ac2250cbSAndy Fiddaman 	    sa, sizeof (*sa) - 4);
196*ac2250cbSAndy Fiddaman }
197*ac2250cbSAndy Fiddaman 
198*ac2250cbSAndy Fiddaman static void
t_param_fuzz(void)199*ac2250cbSAndy Fiddaman t_param_fuzz(void)
200*ac2250cbSAndy Fiddaman {
201*ac2250cbSAndy Fiddaman 	union {
202*ac2250cbSAndy Fiddaman 		spawn_args_t sa;
203*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_args_t) + SA_DATA];
204*ac2250cbSAndy Fiddaman 	} au;
205*ac2250cbSAndy Fiddaman 	union {
206*ac2250cbSAndy Fiddaman 		spawn_param_t sp;
207*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_param_t) + SP_DATA];
208*ac2250cbSAndy Fiddaman 	} pu;
209*ac2250cbSAndy Fiddaman 	spawn_param_t *sp = &pu.sp;
210*ac2250cbSAndy Fiddaman 	uint32_t sasize = valid_args(&au.sa);
211*ac2250cbSAndy Fiddaman 	uint32_t spsize = sizeof (*sp) + SP_DATA;
212*ac2250cbSAndy Fiddaman 	kfile_attr_t *kfa;
213*ac2250cbSAndy Fiddaman 
214*ac2250cbSAndy Fiddaman 	/* Size field disagreeing with the system call argument. */
215*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
216*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize + 4;
217*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
218*ac2250cbSAndy Fiddaman 	expect_err("param-size-mismatch", EINVAL, "/usr/bin/true",
219*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
220*ac2250cbSAndy Fiddaman 
221*ac2250cbSAndy Fiddaman 	/* Attribute region out of bounds. */
222*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
223*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
224*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
225*ac2250cbSAndy Fiddaman 	sp->sp_attr_off = 60;
226*ac2250cbSAndy Fiddaman 	sp->sp_attr_len = sizeof (spawn_attr_t);
227*ac2250cbSAndy Fiddaman 	expect_err("param-attr-off", EINVAL, "/usr/bin/true",
228*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
229*ac2250cbSAndy Fiddaman 
230*ac2250cbSAndy Fiddaman 	/* Attribute length that is not sizeof (spawn_attr_t). */
231*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
232*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
233*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
234*ac2250cbSAndy Fiddaman 	sp->sp_attr_len = 8;
235*ac2250cbSAndy Fiddaman 	expect_err("param-attr-len", EINVAL, "/usr/bin/true",
236*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
237*ac2250cbSAndy Fiddaman 
238*ac2250cbSAndy Fiddaman 	/* Undefined attribute flags. */
239*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
240*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
241*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
242*ac2250cbSAndy Fiddaman 	sp->sp_attr_off = 0;
243*ac2250cbSAndy Fiddaman 	sp->sp_attr_len = sizeof (spawn_attr_t);
244*ac2250cbSAndy Fiddaman 	((spawn_attr_t *)&sp->sp_data[0])->sa_psflags = ~0;
245*ac2250cbSAndy Fiddaman 	expect_err("param-attr-flags", EINVAL, "/usr/bin/true",
246*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
247*ac2250cbSAndy Fiddaman 
248*ac2250cbSAndy Fiddaman 	/* File action record with a zero length. */
249*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
250*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
251*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
252*ac2250cbSAndy Fiddaman 	sp->sp_fattr_cnt = 2;
253*ac2250cbSAndy Fiddaman 	expect_err("param-fattr-len", EINVAL, "/usr/bin/true",
254*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
255*ac2250cbSAndy Fiddaman 
256*ac2250cbSAndy Fiddaman 	/* File action record with a bad type. */
257*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
258*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
259*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
260*ac2250cbSAndy Fiddaman 	sp->sp_fattr_cnt = 1;
261*ac2250cbSAndy Fiddaman 	kfa = (kfile_attr_t *)&sp->sp_data[0];
262*ac2250cbSAndy Fiddaman 	kfa->kfa_len = sizeof (*kfa);
263*ac2250cbSAndy Fiddaman 	kfa->kfa_type = 666;
264*ac2250cbSAndy Fiddaman 	expect_err("param-fattr-type", EINVAL, "/usr/bin/true",
265*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
266*ac2250cbSAndy Fiddaman 
267*ac2250cbSAndy Fiddaman 	/* FA_CHDIR with an unterminated path. */
268*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
269*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
270*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
271*ac2250cbSAndy Fiddaman 	sp->sp_fattr_cnt = 1;
272*ac2250cbSAndy Fiddaman 	kfa = (kfile_attr_t *)&sp->sp_data[0];
273*ac2250cbSAndy Fiddaman 	kfa->kfa_type = FA_CHDIR;
274*ac2250cbSAndy Fiddaman 	kfa->kfa_pathsize = 4;
275*ac2250cbSAndy Fiddaman 	kfa->kfa_len = sizeof (*kfa) + 4;
276*ac2250cbSAndy Fiddaman 	(void) memcpy(kfa->kfa_path, "/tmp", 4);	/* no NUL */
277*ac2250cbSAndy Fiddaman 	expect_err("param-chdir-nul", EINVAL, "/usr/bin/true",
278*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
279*ac2250cbSAndy Fiddaman 
280*ac2250cbSAndy Fiddaman 	/* Unterminated shell and search path strings. */
281*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
282*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
283*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
284*ac2250cbSAndy Fiddaman 	sp->sp_shell_off = 0;
285*ac2250cbSAndy Fiddaman 	sp->sp_shell_len = 4;
286*ac2250cbSAndy Fiddaman 	(void) memcpy(sp->sp_data, "/bin", 4);
287*ac2250cbSAndy Fiddaman 	expect_err("param-shell-nul", EINVAL, "/usr/bin/true",
288*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
289*ac2250cbSAndy Fiddaman 
290*ac2250cbSAndy Fiddaman 	(void) memset(&pu, 0, sizeof (pu));
291*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
292*ac2250cbSAndy Fiddaman 	sp->sp_datalen = SP_DATA;
293*ac2250cbSAndy Fiddaman 	sp->sp_path_off = 62;
294*ac2250cbSAndy Fiddaman 	sp->sp_path_len = 8;
295*ac2250cbSAndy Fiddaman 	expect_err("param-path-off", EINVAL, "/usr/bin/true",
296*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
297*ac2250cbSAndy Fiddaman 
298*ac2250cbSAndy Fiddaman 	expect_err("param-e2big", E2BIG, "/usr/bin/true",
299*ac2250cbSAndy Fiddaman 	    sp, 0x400000, &au.sa, sasize);
300*ac2250cbSAndy Fiddaman 
301*ac2250cbSAndy Fiddaman 	expect_err("param-short", EINVAL, "/usr/bin/true",
302*ac2250cbSAndy Fiddaman 	    sp, sizeof (*sp) - 4, &au.sa, sasize);
303*ac2250cbSAndy Fiddaman }
304*ac2250cbSAndy Fiddaman 
305*ac2250cbSAndy Fiddaman /*
306*ac2250cbSAndy Fiddaman  * Build a spawn_param_t carrying an attribute region with
307*ac2250cbSAndy Fiddaman  * POSIX_SPAWN_SETSCHEDPARAM set and a scheduling region with the given
308*ac2250cbSAndy Fiddaman  * operation.
309*ac2250cbSAndy Fiddaman  */
310*ac2250cbSAndy Fiddaman static kspawn_sched_t *
valid_sched(spawn_param_t * sp,uint32_t spsize,int op)311*ac2250cbSAndy Fiddaman valid_sched(spawn_param_t *sp, uint32_t spsize, int op)
312*ac2250cbSAndy Fiddaman {
313*ac2250cbSAndy Fiddaman 	spawn_attr_t *spa;
314*ac2250cbSAndy Fiddaman 	kspawn_sched_t *ks;
315*ac2250cbSAndy Fiddaman 
316*ac2250cbSAndy Fiddaman 	(void) memset(sp, 0, spsize);
317*ac2250cbSAndy Fiddaman 	sp->sp_size = spsize;
318*ac2250cbSAndy Fiddaman 	sp->sp_datalen = spsize - sizeof (*sp);
319*ac2250cbSAndy Fiddaman 	sp->sp_attr_off = 0;
320*ac2250cbSAndy Fiddaman 	sp->sp_attr_len = sizeof (spawn_attr_t);
321*ac2250cbSAndy Fiddaman 	spa = (spawn_attr_t *)&sp->sp_data[0];
322*ac2250cbSAndy Fiddaman 	spa->sa_psflags = POSIX_SPAWN_SETSCHEDPARAM;
323*ac2250cbSAndy Fiddaman 	sp->sp_sched_off = sizeof (spawn_attr_t);
324*ac2250cbSAndy Fiddaman 	sp->sp_sched_len = sizeof (kspawn_sched_t);
325*ac2250cbSAndy Fiddaman 	ks = (kspawn_sched_t *)&sp->sp_data[sp->sp_sched_off];
326*ac2250cbSAndy Fiddaman 	ks->ksched_op = op;
327*ac2250cbSAndy Fiddaman 
328*ac2250cbSAndy Fiddaman 	return (ks);
329*ac2250cbSAndy Fiddaman }
330*ac2250cbSAndy Fiddaman 
331*ac2250cbSAndy Fiddaman static void
t_sched_fuzz(void)332*ac2250cbSAndy Fiddaman t_sched_fuzz(void)
333*ac2250cbSAndy Fiddaman {
334*ac2250cbSAndy Fiddaman 	union {
335*ac2250cbSAndy Fiddaman 		spawn_args_t sa;
336*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_args_t) + SA_DATA];
337*ac2250cbSAndy Fiddaman 	} au;
338*ac2250cbSAndy Fiddaman 	union {
339*ac2250cbSAndy Fiddaman 		spawn_param_t sp;
340*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_param_t) + 128];
341*ac2250cbSAndy Fiddaman 	} pu;
342*ac2250cbSAndy Fiddaman 	spawn_param_t *sp = &pu.sp;
343*ac2250cbSAndy Fiddaman 	uint32_t sasize = valid_args(&au.sa);
344*ac2250cbSAndy Fiddaman 	uint32_t spsize = sizeof (*sp) + 128;
345*ac2250cbSAndy Fiddaman 	kspawn_sched_t *ks;
346*ac2250cbSAndy Fiddaman 
347*ac2250cbSAndy Fiddaman 	/* Scheduling flag set but no scheduling region. */
348*ac2250cbSAndy Fiddaman 	(void) valid_sched(sp, spsize, KSCHED_PARMS);
349*ac2250cbSAndy Fiddaman 	sp->sp_sched_off = sp->sp_sched_len = 0;
350*ac2250cbSAndy Fiddaman 	expect_err("sched-missing", EINVAL, "/usr/bin/true",
351*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
352*ac2250cbSAndy Fiddaman 
353*ac2250cbSAndy Fiddaman 	/* Scheduling region present without a scheduling flag. */
354*ac2250cbSAndy Fiddaman 	(void) valid_sched(sp, spsize, KSCHED_PARMS);
355*ac2250cbSAndy Fiddaman 	((spawn_attr_t *)&sp->sp_data[0])->sa_psflags = 0;
356*ac2250cbSAndy Fiddaman 	expect_err("sched-no-flags", EINVAL, "/usr/bin/true",
357*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
358*ac2250cbSAndy Fiddaman 
359*ac2250cbSAndy Fiddaman 	/* Scheduling region with the wrong length. */
360*ac2250cbSAndy Fiddaman 	(void) valid_sched(sp, spsize, KSCHED_PARMS);
361*ac2250cbSAndy Fiddaman 	sp->sp_sched_len = 8;
362*ac2250cbSAndy Fiddaman 	expect_err("sched-len", EINVAL, "/usr/bin/true",
363*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
364*ac2250cbSAndy Fiddaman 
365*ac2250cbSAndy Fiddaman 	/* Scheduling region out of bounds. */
366*ac2250cbSAndy Fiddaman 	(void) valid_sched(sp, spsize, KSCHED_PARMS);
367*ac2250cbSAndy Fiddaman 	sp->sp_sched_off = sp->sp_datalen - 4;
368*ac2250cbSAndy Fiddaman 	expect_err("sched-off", EINVAL, "/usr/bin/true",
369*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
370*ac2250cbSAndy Fiddaman 
371*ac2250cbSAndy Fiddaman 	/* Invalid operation. */
372*ac2250cbSAndy Fiddaman 	(void) valid_sched(sp, spsize, 0);
373*ac2250cbSAndy Fiddaman 	expect_err("sched-op", EINVAL, "/usr/bin/true",
374*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
375*ac2250cbSAndy Fiddaman 
376*ac2250cbSAndy Fiddaman 	/* A priority change must be a set operation. */
377*ac2250cbSAndy Fiddaman 	ks = valid_sched(sp, spsize, KSCHED_PRIO);
378*ac2250cbSAndy Fiddaman 	ks->ksched_prio.pc_op = PC_GETPRIO;
379*ac2250cbSAndy Fiddaman 	ks->ksched_prio.pc_cid = 1;
380*ac2250cbSAndy Fiddaman 	expect_err("sched-prio-op", EINVAL, "/usr/bin/true",
381*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
382*ac2250cbSAndy Fiddaman 
383*ac2250cbSAndy Fiddaman 	/*
384*ac2250cbSAndy Fiddaman 	 * Class IDs out of range. These are not caught until the child
385*ac2250cbSAndy Fiddaman 	 * applies the attributes to itself, exercising the error handshake.
386*ac2250cbSAndy Fiddaman 	 */
387*ac2250cbSAndy Fiddaman 	ks = valid_sched(sp, spsize, KSCHED_PRIO);
388*ac2250cbSAndy Fiddaman 	ks->ksched_prio.pc_op = PC_SETPRIO;
389*ac2250cbSAndy Fiddaman 	ks->ksched_prio.pc_cid = 999;
390*ac2250cbSAndy Fiddaman 	expect_err("sched-prio-cid", EINVAL, "/usr/bin/true",
391*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
392*ac2250cbSAndy Fiddaman 
393*ac2250cbSAndy Fiddaman 	ks = valid_sched(sp, spsize, KSCHED_PARMS);
394*ac2250cbSAndy Fiddaman 	ks->ksched_parms.pc_cid = 999;
395*ac2250cbSAndy Fiddaman 	expect_err("sched-parms-cid", EINVAL, "/usr/bin/true",
396*ac2250cbSAndy Fiddaman 	    sp, spsize, &au.sa, sasize);
397*ac2250cbSAndy Fiddaman }
398*ac2250cbSAndy Fiddaman 
399*ac2250cbSAndy Fiddaman /*
400*ac2250cbSAndy Fiddaman  * A child killed immediately after spawn(2) returns must be waitable and
401*ac2250cbSAndy Fiddaman  * must never leave the parent stuck. We try a few times to give us a fighting
402*ac2250cbSAndy Fiddaman  * chance of catching it.
403*ac2250cbSAndy Fiddaman  */
404*ac2250cbSAndy Fiddaman static void
t_kill_race(void)405*ac2250cbSAndy Fiddaman t_kill_race(void)
406*ac2250cbSAndy Fiddaman {
407*ac2250cbSAndy Fiddaman 	union {
408*ac2250cbSAndy Fiddaman 		spawn_args_t sa;
409*ac2250cbSAndy Fiddaman 		char pad[sizeof (spawn_args_t) + SA_DATA];
410*ac2250cbSAndy Fiddaman 	} au;
411*ac2250cbSAndy Fiddaman 	uint32_t sasize = valid_args(&au.sa);
412*ac2250cbSAndy Fiddaman 
413*ac2250cbSAndy Fiddaman 	/*
414*ac2250cbSAndy Fiddaman 	 * Spawn a child and immediately kill it, over and over, to exercise
415*ac2250cbSAndy Fiddaman 	 * the window between creation and exec. valid_args() supplies only
416*ac2250cbSAndy Fiddaman 	 * argv[0], so sleep runs with no duration. That does not matter,
417*ac2250cbSAndy Fiddaman 	 * because we SIGKILL it at once - all we need is a live PID to race
418*ac2250cbSAndy Fiddaman 	 * against.
419*ac2250cbSAndy Fiddaman 	 */
420*ac2250cbSAndy Fiddaman 	for (int i = 0; i < 100; i++) {
421*ac2250cbSAndy Fiddaman 		int status;
422*ac2250cbSAndy Fiddaman 		pid_t pid;
423*ac2250cbSAndy Fiddaman 
424*ac2250cbSAndy Fiddaman 		pid = raw_spawn("/usr/bin/sleep", NULL, 0, &au.sa, sasize);
425*ac2250cbSAndy Fiddaman 		if (pid == -1) {
426*ac2250cbSAndy Fiddaman 			TFAIL("kill-race", "spawn failed: %s",
427*ac2250cbSAndy Fiddaman 			    strerror(errno));
428*ac2250cbSAndy Fiddaman 			return;
429*ac2250cbSAndy Fiddaman 		}
430*ac2250cbSAndy Fiddaman 		(void) kill(pid, SIGKILL);
431*ac2250cbSAndy Fiddaman 		if (waitpid(pid, &status, 0) != pid) {
432*ac2250cbSAndy Fiddaman 			TFAIL("kill-race", "waitpid: %s", strerror(errno));
433*ac2250cbSAndy Fiddaman 			return;
434*ac2250cbSAndy Fiddaman 		}
435*ac2250cbSAndy Fiddaman 		if (!WIFSIGNALED(status) &&
436*ac2250cbSAndy Fiddaman 		    !(WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
437*ac2250cbSAndy Fiddaman 			TFAIL("kill-race", "iteration %d: status %#x",
438*ac2250cbSAndy Fiddaman 			    i, status);
439*ac2250cbSAndy Fiddaman 			return;
440*ac2250cbSAndy Fiddaman 		}
441*ac2250cbSAndy Fiddaman 	}
442*ac2250cbSAndy Fiddaman 	TPASS("kill-race");
443*ac2250cbSAndy Fiddaman }
444*ac2250cbSAndy Fiddaman 
445*ac2250cbSAndy Fiddaman int
main(void)446*ac2250cbSAndy Fiddaman main(void)
447*ac2250cbSAndy Fiddaman {
448*ac2250cbSAndy Fiddaman 	t_valid();
449*ac2250cbSAndy Fiddaman 	t_args_fuzz();
450*ac2250cbSAndy Fiddaman 	t_param_fuzz();
451*ac2250cbSAndy Fiddaman 	t_sched_fuzz();
452*ac2250cbSAndy Fiddaman 	t_kill_race();
453*ac2250cbSAndy Fiddaman 
454*ac2250cbSAndy Fiddaman 	if (failures == 0) {
455*ac2250cbSAndy Fiddaman 		(void) printf("All tests passed\n");
456*ac2250cbSAndy Fiddaman 		return (EXIT_SUCCESS);
457*ac2250cbSAndy Fiddaman 	}
458*ac2250cbSAndy Fiddaman 
459*ac2250cbSAndy Fiddaman 	(void) fprintf(stderr, "%u test(s) failed\n", failures);
460*ac2250cbSAndy Fiddaman 	return (EXIT_FAILURE);
461*ac2250cbSAndy Fiddaman }
462