xref: /freebsd/tests/sys/pmc/pmc_credexec_test.c (revision bea7b932b9eeaff39393347e0600b982fd859a92)
1*bea7b932SAlexander Leidinger /*-
2*bea7b932SAlexander Leidinger  * SPDX-License-Identifier: BSD-2-Clause
3*bea7b932SAlexander Leidinger  *
4*bea7b932SAlexander Leidinger  * Copyright (c) 2026 Alexander Leidinger <netchild@FreeBSD.org>
5*bea7b932SAlexander Leidinger  *
6*bea7b932SAlexander Leidinger  * Redistribution and use in source and binary forms, with or without
7*bea7b932SAlexander Leidinger  * modification, are permitted provided that the following conditions
8*bea7b932SAlexander Leidinger  * are met:
9*bea7b932SAlexander Leidinger  * 1. Redistributions of source code must retain the above copyright
10*bea7b932SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer.
11*bea7b932SAlexander Leidinger  * 2. Redistributions in binary form must reproduce the above copyright
12*bea7b932SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer in the
13*bea7b932SAlexander Leidinger  *    documentation and/or other advertising materials provided with the
14*bea7b932SAlexander Leidinger  *    distribution.
15*bea7b932SAlexander Leidinger  *
16*bea7b932SAlexander Leidinger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*bea7b932SAlexander Leidinger  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*bea7b932SAlexander Leidinger  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*bea7b932SAlexander Leidinger  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*bea7b932SAlexander Leidinger  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*bea7b932SAlexander Leidinger  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*bea7b932SAlexander Leidinger  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*bea7b932SAlexander Leidinger  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*bea7b932SAlexander Leidinger  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*bea7b932SAlexander Leidinger  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*bea7b932SAlexander Leidinger  * SUCH DAMAGE.
27*bea7b932SAlexander Leidinger  */
28*bea7b932SAlexander Leidinger 
29*bea7b932SAlexander Leidinger /**
30*bea7b932SAlexander Leidinger  * @file
31*bea7b932SAlexander Leidinger  * Credential-transition tests for a process-mode PMC across exec(), the
32*bea7b932SAlexander Leidinger  * companion to pmc_exec_test.c.  Where that file proves the driver drops a
33*bea7b932SAlexander Leidinger  * PMC when its target execs into credentials its owner may no longer
34*bea7b932SAlexander Leidinger  * trace, these prove the two cases the drop must not overreach into: an
35*bea7b932SAlexander Leidinger  * exec that changes no credentials keeps the PMC, and an exec of a set-id
36*bea7b932SAlexander Leidinger  * binary whose credential change the kernel then suppresses, because the
37*bea7b932SAlexander Leidinger  * target is ptrace(2)d, keeps it too - do_execve() derives the set-id from
38*bea7b932SAlexander Leidinger  * the image alone and suppresses it afterwards, clearing P_SUGID and
39*bea7b932SAlexander Leidinger  * leaving the ids untouched, so the owner is still entitled.  The third
40*bea7b932SAlexander Leidinger  * case covers fexecve(2), which reaches the same hook as execve(2).
41*bea7b932SAlexander Leidinger  * These exercise what FreeBSD-SA-26:56.hwpmc reworked, not what it fixed.
42*bea7b932SAlexander Leidinger  *
43*bea7b932SAlexander Leidinger  * As in pmc_exec_test.c the owner must be unprivileged, and the test must
44*bea7b932SAlexander Leidinger  * not lower its own privilege: setuid(2) sets P_SUGID, fork(2) passes it
45*bea7b932SAlexander Leidinger  * to the target, and p_candebug() then refuses that target to its own
46*bea7b932SAlexander Leidinger  * owner.  Not covered, because each needs privileged setup a require.user
47*bea7b932SAlexander Leidinger  * body cannot build: a set-id exec on a nosuid mount, a set-id #!
48*bea7b932SAlexander Leidinger  * interpreter, and the two jail transitions.
49*bea7b932SAlexander Leidinger  */
50*bea7b932SAlexander Leidinger 
51*bea7b932SAlexander Leidinger #include <sys/types.h>
52*bea7b932SAlexander Leidinger #include <sys/ptrace.h>
53*bea7b932SAlexander Leidinger #include <sys/stat.h>
54*bea7b932SAlexander Leidinger #include <sys/wait.h>
55*bea7b932SAlexander Leidinger 
56*bea7b932SAlexander Leidinger #include <errno.h>
57*bea7b932SAlexander Leidinger #include <fcntl.h>
58*bea7b932SAlexander Leidinger #include <pmc.h>
59*bea7b932SAlexander Leidinger #include <signal.h>
60*bea7b932SAlexander Leidinger #include <stdlib.h>
61*bea7b932SAlexander Leidinger #include <string.h>
62*bea7b932SAlexander Leidinger #include <unistd.h>
63*bea7b932SAlexander Leidinger 
64*bea7b932SAlexander Leidinger #include <atf-c.h>
65*bea7b932SAlexander Leidinger 
66*bea7b932SAlexander Leidinger static const char *counting_events[] = {
67*bea7b932SAlexander Leidinger 	"instructions",
68*bea7b932SAlexander Leidinger 	"cycles",
69*bea7b932SAlexander Leidinger 	"branches",
70*bea7b932SAlexander Leidinger 	"unhalted-core-cycles",
71*bea7b932SAlexander Leidinger 	"inst_retired.any",
72*bea7b932SAlexander Leidinger 	"cpu_clk_unhalted.thread",
73*bea7b932SAlexander Leidinger 	"ls_not_halted_cyc",
74*bea7b932SAlexander Leidinger 	NULL
75*bea7b932SAlexander Leidinger };
76*bea7b932SAlexander Leidinger 
77*bea7b932SAlexander Leidinger /**
78*bea7b932SAlexander Leidinger  * @internal
79*bea7b932SAlexander Leidinger  * Exec targets.  'setid' is the bit the entry must carry to exercise the
80*bea7b932SAlexander Leidinger  * case; the argument vectors are chosen so the program touches nothing.
81*bea7b932SAlexander Leidinger  * A target that blocks on stdin stays alive after the exec so that the
82*bea7b932SAlexander Leidinger  * still-attached question can be asked; the caller holds the read end open.
83*bea7b932SAlexander Leidinger  */
84*bea7b932SAlexander Leidinger struct exec_target {
85*bea7b932SAlexander Leidinger 	const char	*path;
86*bea7b932SAlexander Leidinger 	const char	*const argv[5];
87*bea7b932SAlexander Leidinger 	mode_t		 setid;
88*bea7b932SAlexander Leidinger 	int		 blocks_on_stdin;
89*bea7b932SAlexander Leidinger };
90*bea7b932SAlexander Leidinger 
91*bea7b932SAlexander Leidinger /**
92*bea7b932SAlexander Leidinger  * @internal
93*bea7b932SAlexander Leidinger  * Set-gid, blocks on stdin: stays alive so "still attached?" is answerable.
94*bea7b932SAlexander Leidinger  */
95*bea7b932SAlexander Leidinger static const struct exec_target setgid_targets[] = {
96*bea7b932SAlexander Leidinger 	{ "/usr/bin/wall", { "wall", NULL }, S_ISGID, 1 },
97*bea7b932SAlexander Leidinger 	{ NULL, { NULL }, 0, 0 }
98*bea7b932SAlexander Leidinger };
99*bea7b932SAlexander Leidinger 
100*bea7b932SAlexander Leidinger /** Set-uid, exits at once: for a drop-case, completing without a panic is
101*bea7b932SAlexander Leidinger  * @internal
102*bea7b932SAlexander Leidinger  * the whole assertion. */
103*bea7b932SAlexander Leidinger static const struct exec_target setuid_targets[] = {
104*bea7b932SAlexander Leidinger 	{ "/sbin/ping", { "ping", "-c", "1", "127.0.0.1", NULL }, S_ISUID, 0 },
105*bea7b932SAlexander Leidinger 	{ NULL, { NULL }, 0, 0 }
106*bea7b932SAlexander Leidinger };
107*bea7b932SAlexander Leidinger 
108*bea7b932SAlexander Leidinger /** An ordinary, non-set-id target that blocks: sleep ignores stdin but
109*bea7b932SAlexander Leidinger  * @internal
110*bea7b932SAlexander Leidinger  * stays alive on its own, which is all exec_ordinary_keeps_pmc needs. */
111*bea7b932SAlexander Leidinger static const struct exec_target ordinary_targets[] = {
112*bea7b932SAlexander Leidinger 	{ "/bin/sleep", { "sleep", "30", NULL }, 0, 0 },
113*bea7b932SAlexander Leidinger 	{ NULL, { NULL }, 0, 0 }
114*bea7b932SAlexander Leidinger };
115*bea7b932SAlexander Leidinger 
116*bea7b932SAlexander Leidinger static const struct exec_target *
pick_target(const struct exec_target * tab,bool need_setid)117*bea7b932SAlexander Leidinger pick_target(const struct exec_target *tab, bool need_setid)
118*bea7b932SAlexander Leidinger {
119*bea7b932SAlexander Leidinger 	struct stat sb;
120*bea7b932SAlexander Leidinger 	int i;
121*bea7b932SAlexander Leidinger 
122*bea7b932SAlexander Leidinger 	for (i = 0; tab[i].path != NULL; i++) {
123*bea7b932SAlexander Leidinger 		if (stat(tab[i].path, &sb) != 0)
124*bea7b932SAlexander Leidinger 			continue;
125*bea7b932SAlexander Leidinger 		if (need_setid && (sb.st_mode & tab[i].setid) == 0)
126*bea7b932SAlexander Leidinger 			continue;
127*bea7b932SAlexander Leidinger 		return (&tab[i]);
128*bea7b932SAlexander Leidinger 	}
129*bea7b932SAlexander Leidinger 	return (NULL);
130*bea7b932SAlexander Leidinger }
131*bea7b932SAlexander Leidinger 
132*bea7b932SAlexander Leidinger static pmc_id_t
allocate_counting_pmc(void)133*bea7b932SAlexander Leidinger allocate_counting_pmc(void)
134*bea7b932SAlexander Leidinger {
135*bea7b932SAlexander Leidinger 	pmc_id_t id = PMC_ID_INVALID;
136*bea7b932SAlexander Leidinger 	int i;
137*bea7b932SAlexander Leidinger 
138*bea7b932SAlexander Leidinger 	for (i = 0; counting_events[i] != NULL; i++) {
139*bea7b932SAlexander Leidinger 		if (pmc_allocate(counting_events[i], PMC_MODE_TC, 0,
140*bea7b932SAlexander Leidinger 		    PMC_CPU_ANY, &id, 0) == 0)
141*bea7b932SAlexander Leidinger 			return (id);
142*bea7b932SAlexander Leidinger 	}
143*bea7b932SAlexander Leidinger 	return (PMC_ID_INVALID);
144*bea7b932SAlexander Leidinger }
145*bea7b932SAlexander Leidinger 
146*bea7b932SAlexander Leidinger static void
spin(void)147*bea7b932SAlexander Leidinger spin(void)
148*bea7b932SAlexander Leidinger {
149*bea7b932SAlexander Leidinger 	volatile unsigned long s = 0;
150*bea7b932SAlexander Leidinger 	int i;
151*bea7b932SAlexander Leidinger 
152*bea7b932SAlexander Leidinger 	for (i = 0; i < 2000000; i++)
153*bea7b932SAlexander Leidinger 		s += i;
154*bea7b932SAlexander Leidinger }
155*bea7b932SAlexander Leidinger 
156*bea7b932SAlexander Leidinger static void
require_unprivileged_owner(void)157*bea7b932SAlexander Leidinger require_unprivileged_owner(void)
158*bea7b932SAlexander Leidinger {
159*bea7b932SAlexander Leidinger 
160*bea7b932SAlexander Leidinger 	if (geteuid() == 0)
161*bea7b932SAlexander Leidinger 		atf_tc_skip("the PMC owner must be unprivileged: root may "
162*bea7b932SAlexander Leidinger 		    "trace any credentials, so the check under test is never "
163*bea7b932SAlexander Leidinger 		    "reached");
164*bea7b932SAlexander Leidinger }
165*bea7b932SAlexander Leidinger 
166*bea7b932SAlexander Leidinger /**
167*bea7b932SAlexander Leidinger  * @internal
168*bea7b932SAlexander Leidinger  * How the target should behave between fork and the measured exec.
169*bea7b932SAlexander Leidinger  */
170*bea7b932SAlexander Leidinger enum exec_via {
171*bea7b932SAlexander Leidinger 	VIA_EXECVE,	/* plain execve(2) */
172*bea7b932SAlexander Leidinger 	VIA_FEXECVE,	/* fexecve(2) of an fd opened O_EXEC */
173*bea7b932SAlexander Leidinger 	VIA_TRACED,	/* PT_TRACE_ME first, so the exec is credential-safe */
174*bea7b932SAlexander Leidinger };
175*bea7b932SAlexander Leidinger 
176*bea7b932SAlexander Leidinger /**
177*bea7b932SAlexander Leidinger  * @internal
178*bea7b932SAlexander Leidinger  * Fork a target, attach a running counting PMC, and let it exec.  On
179*bea7b932SAlexander Leidinger  * return the PMC is released.  When 'inspect' is set the target is expected
180*bea7b932SAlexander Leidinger  * to stay alive past the exec, and *still_attached reports whether the PMC
181*bea7b932SAlexander Leidinger  * survived it; a drop-case passes inspect=0 for a target that exits at once
182*bea7b932SAlexander Leidinger  * (it has already torn its own descriptor down, so there is nothing to ask).
183*bea7b932SAlexander Leidinger  */
184*bea7b932SAlexander Leidinger static void
run_target(const struct exec_target * t,enum exec_via via,int inspect,int * still_attached)185*bea7b932SAlexander Leidinger run_target(const struct exec_target *t, enum exec_via via, int inspect,
186*bea7b932SAlexander Leidinger     int *still_attached)
187*bea7b932SAlexander Leidinger {
188*bea7b932SAlexander Leidinger 	pmc_id_t id;
189*bea7b932SAlexander Leidinger 	pid_t target;
190*bea7b932SAlexander Leidinger 	char token;
191*bea7b932SAlexander Leidinger 	int gopipe[2], inpipe[2], status;
192*bea7b932SAlexander Leidinger 
193*bea7b932SAlexander Leidinger 	ATF_REQUIRE(pipe(gopipe) == 0);
194*bea7b932SAlexander Leidinger 	ATF_REQUIRE(pipe(inpipe) == 0);
195*bea7b932SAlexander Leidinger 	ATF_REQUIRE((target = fork()) >= 0);
196*bea7b932SAlexander Leidinger 
197*bea7b932SAlexander Leidinger 	if (target == 0) {
198*bea7b932SAlexander Leidinger 		int fd;
199*bea7b932SAlexander Leidinger 
200*bea7b932SAlexander Leidinger 		(void)close(gopipe[1]);
201*bea7b932SAlexander Leidinger 		if (t->blocks_on_stdin)
202*bea7b932SAlexander Leidinger 			(void)dup2(inpipe[0], STDIN_FILENO);
203*bea7b932SAlexander Leidinger 		(void)close(inpipe[1]);
204*bea7b932SAlexander Leidinger 		if (via == VIA_TRACED)
205*bea7b932SAlexander Leidinger 			(void)ptrace(PT_TRACE_ME, 0, NULL, 0);
206*bea7b932SAlexander Leidinger 		if (read(gopipe[0], &token, 1) != 1)
207*bea7b932SAlexander Leidinger 			_exit(1);
208*bea7b932SAlexander Leidinger 		spin();
209*bea7b932SAlexander Leidinger 		if (via == VIA_FEXECVE) {
210*bea7b932SAlexander Leidinger 			if ((fd = open(t->path, O_EXEC)) < 0)
211*bea7b932SAlexander Leidinger 				_exit(1);
212*bea7b932SAlexander Leidinger 			(void)fexecve(fd, __DECONST(char **, t->argv), NULL);
213*bea7b932SAlexander Leidinger 		} else {
214*bea7b932SAlexander Leidinger 			(void)execv(t->path, __DECONST(char **, t->argv));
215*bea7b932SAlexander Leidinger 		}
216*bea7b932SAlexander Leidinger 		_exit(1);
217*bea7b932SAlexander Leidinger 	}
218*bea7b932SAlexander Leidinger 	(void)close(gopipe[0]);
219*bea7b932SAlexander Leidinger 	(void)close(inpipe[0]);
220*bea7b932SAlexander Leidinger 
221*bea7b932SAlexander Leidinger 	if (pmc_init() != 0) {
222*bea7b932SAlexander Leidinger 		(void)kill(target, SIGKILL);
223*bea7b932SAlexander Leidinger 		(void)waitpid(target, &status, 0);
224*bea7b932SAlexander Leidinger 		atf_tc_skip("hwpmc(4) is not available");
225*bea7b932SAlexander Leidinger 	}
226*bea7b932SAlexander Leidinger 	if ((id = allocate_counting_pmc()) == PMC_ID_INVALID) {
227*bea7b932SAlexander Leidinger 		(void)kill(target, SIGKILL);
228*bea7b932SAlexander Leidinger 		(void)waitpid(target, &status, 0);
229*bea7b932SAlexander Leidinger 		atf_tc_skip("no process-mode counting event is allocatable");
230*bea7b932SAlexander Leidinger 	}
231*bea7b932SAlexander Leidinger 
232*bea7b932SAlexander Leidinger 	ATF_REQUIRE_MSG(pmc_attach(id, target) == 0, "pmc_attach: %s",
233*bea7b932SAlexander Leidinger 	    strerror(errno));
234*bea7b932SAlexander Leidinger 	ATF_REQUIRE(pmc_start(id) == 0);
235*bea7b932SAlexander Leidinger 
236*bea7b932SAlexander Leidinger 	/* Into the exec. */
237*bea7b932SAlexander Leidinger 	ATF_REQUIRE(write(gopipe[1], "g", 1) == 1);
238*bea7b932SAlexander Leidinger 	(void)close(gopipe[1]);
239*bea7b932SAlexander Leidinger 
240*bea7b932SAlexander Leidinger 	if (via == VIA_TRACED) {
241*bea7b932SAlexander Leidinger 		/*
242*bea7b932SAlexander Leidinger 		 * The exec of a set-id binary under a tracer stops the
243*bea7b932SAlexander Leidinger 		 * target with SIGTRAP and leaves its credentials unchanged.
244*bea7b932SAlexander Leidinger 		 * Reap that stop and detach the tracer; the hwpmc decision
245*bea7b932SAlexander Leidinger 		 * was already taken at exec time, so from here the target
246*bea7b932SAlexander Leidinger 		 * runs as an ordinary blocked process.
247*bea7b932SAlexander Leidinger 		 */
248*bea7b932SAlexander Leidinger 		ATF_REQUIRE(waitpid(target, &status, 0) == target);
249*bea7b932SAlexander Leidinger 		ATF_REQUIRE_MSG(WIFSTOPPED(status),
250*bea7b932SAlexander Leidinger 		    "traced target did not stop at exec (status 0x%x)", status);
251*bea7b932SAlexander Leidinger 		ATF_REQUIRE_MSG(ptrace(PT_DETACH, target, (caddr_t)1, 0) == 0,
252*bea7b932SAlexander Leidinger 		    "PT_DETACH: %s", strerror(errno));
253*bea7b932SAlexander Leidinger 	}
254*bea7b932SAlexander Leidinger 
255*bea7b932SAlexander Leidinger 	if (inspect) {
256*bea7b932SAlexander Leidinger 		(void)usleep(400000);
257*bea7b932SAlexander Leidinger 		errno = 0;
258*bea7b932SAlexander Leidinger 		*still_attached = pmc_detach(id, target) == 0;
259*bea7b932SAlexander Leidinger 		if (!*still_attached)
260*bea7b932SAlexander Leidinger 			ATF_REQUIRE_MSG(errno == ESRCH, "pmc_detach: %s",
261*bea7b932SAlexander Leidinger 			    strerror(errno));
262*bea7b932SAlexander Leidinger 		(void)kill(target, SIGKILL);
263*bea7b932SAlexander Leidinger 	} else {
264*bea7b932SAlexander Leidinger 		*still_attached = 0;
265*bea7b932SAlexander Leidinger 	}
266*bea7b932SAlexander Leidinger 	(void)waitpid(target, &status, 0);
267*bea7b932SAlexander Leidinger 	(void)pmc_release(id);
268*bea7b932SAlexander Leidinger }
269*bea7b932SAlexander Leidinger 
270*bea7b932SAlexander Leidinger ATF_TC(exec_ordinary_keeps_pmc);
ATF_TC_HEAD(exec_ordinary_keeps_pmc,tc)271*bea7b932SAlexander Leidinger ATF_TC_HEAD(exec_ordinary_keeps_pmc, tc)
272*bea7b932SAlexander Leidinger {
273*bea7b932SAlexander Leidinger 
274*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "descr",
275*bea7b932SAlexander Leidinger 	    "a process-mode PMC survives an exec that does not change the "
276*bea7b932SAlexander Leidinger 	    "target's credentials");
277*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
278*bea7b932SAlexander Leidinger }
ATF_TC_BODY(exec_ordinary_keeps_pmc,tc)279*bea7b932SAlexander Leidinger ATF_TC_BODY(exec_ordinary_keeps_pmc, tc)
280*bea7b932SAlexander Leidinger {
281*bea7b932SAlexander Leidinger 	const struct exec_target *t;
282*bea7b932SAlexander Leidinger 	int still_attached;
283*bea7b932SAlexander Leidinger 
284*bea7b932SAlexander Leidinger 	require_unprivileged_owner();
285*bea7b932SAlexander Leidinger 	if ((t = pick_target(ordinary_targets, false)) == NULL)
286*bea7b932SAlexander Leidinger 		atf_tc_skip("no ordinary exec target available");
287*bea7b932SAlexander Leidinger 
288*bea7b932SAlexander Leidinger 	run_target(t, VIA_EXECVE, 1, &still_attached);
289*bea7b932SAlexander Leidinger 	ATF_REQUIRE_MSG(still_attached,
290*bea7b932SAlexander Leidinger 	    "the PMC was dropped across an exec that changed no credentials");
291*bea7b932SAlexander Leidinger }
292*bea7b932SAlexander Leidinger 
293*bea7b932SAlexander Leidinger ATF_TC(exec_setid_traced_keeps_pmc);
ATF_TC_HEAD(exec_setid_traced_keeps_pmc,tc)294*bea7b932SAlexander Leidinger ATF_TC_HEAD(exec_setid_traced_keeps_pmc, tc)
295*bea7b932SAlexander Leidinger {
296*bea7b932SAlexander Leidinger 
297*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "descr",
298*bea7b932SAlexander Leidinger 	    "a process-mode PMC survives its target's exec of a set-id binary "
299*bea7b932SAlexander Leidinger 	    "when tracing suppresses the credential change and the owner "
300*bea7b932SAlexander Leidinger 	    "remains entitled");
301*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
302*bea7b932SAlexander Leidinger }
ATF_TC_BODY(exec_setid_traced_keeps_pmc,tc)303*bea7b932SAlexander Leidinger ATF_TC_BODY(exec_setid_traced_keeps_pmc, tc)
304*bea7b932SAlexander Leidinger {
305*bea7b932SAlexander Leidinger 	const struct exec_target *t;
306*bea7b932SAlexander Leidinger 	int still_attached;
307*bea7b932SAlexander Leidinger 
308*bea7b932SAlexander Leidinger 	require_unprivileged_owner();
309*bea7b932SAlexander Leidinger 	if ((t = pick_target(setgid_targets, true)) == NULL)
310*bea7b932SAlexander Leidinger 		atf_tc_skip("no set-gid exec target available");
311*bea7b932SAlexander Leidinger 
312*bea7b932SAlexander Leidinger 	run_target(t, VIA_TRACED, 1, &still_attached);
313*bea7b932SAlexander Leidinger 	ATF_REQUIRE_MSG(still_attached,
314*bea7b932SAlexander Leidinger 	    "the PMC was dropped although tracing left the target's "
315*bea7b932SAlexander Leidinger 	    "credentials unchanged and its owner still entitled");
316*bea7b932SAlexander Leidinger }
317*bea7b932SAlexander Leidinger 
318*bea7b932SAlexander Leidinger ATF_TC(exec_fexecve_setid_drops_pmc);
ATF_TC_HEAD(exec_fexecve_setid_drops_pmc,tc)319*bea7b932SAlexander Leidinger ATF_TC_HEAD(exec_fexecve_setid_drops_pmc, tc)
320*bea7b932SAlexander Leidinger {
321*bea7b932SAlexander Leidinger 
322*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "descr",
323*bea7b932SAlexander Leidinger 	    "a process-mode PMC is detached when its target reaches a "
324*bea7b932SAlexander Leidinger 	    "credential-changing set-id binary through fexecve(2)");
325*bea7b932SAlexander Leidinger 	atf_tc_set_md_var(tc, "require.user", "unprivileged");
326*bea7b932SAlexander Leidinger }
ATF_TC_BODY(exec_fexecve_setid_drops_pmc,tc)327*bea7b932SAlexander Leidinger ATF_TC_BODY(exec_fexecve_setid_drops_pmc, tc)
328*bea7b932SAlexander Leidinger {
329*bea7b932SAlexander Leidinger 	const struct exec_target *t;
330*bea7b932SAlexander Leidinger 	int still_attached;
331*bea7b932SAlexander Leidinger 
332*bea7b932SAlexander Leidinger 	require_unprivileged_owner();
333*bea7b932SAlexander Leidinger 	if ((t = pick_target(setuid_targets, true)) == NULL)
334*bea7b932SAlexander Leidinger 		atf_tc_skip("no set-uid exec target available");
335*bea7b932SAlexander Leidinger 
336*bea7b932SAlexander Leidinger 	/* Completing at all is the assertion. */
337*bea7b932SAlexander Leidinger 	run_target(t, VIA_FEXECVE, 0, &still_attached);
338*bea7b932SAlexander Leidinger }
339*bea7b932SAlexander Leidinger 
ATF_TP_ADD_TCS(tp)340*bea7b932SAlexander Leidinger ATF_TP_ADD_TCS(tp)
341*bea7b932SAlexander Leidinger {
342*bea7b932SAlexander Leidinger 
343*bea7b932SAlexander Leidinger 	ATF_TP_ADD_TC(tp, exec_ordinary_keeps_pmc);
344*bea7b932SAlexander Leidinger 	ATF_TP_ADD_TC(tp, exec_setid_traced_keeps_pmc);
345*bea7b932SAlexander Leidinger 	ATF_TP_ADD_TC(tp, exec_fexecve_setid_drops_pmc);
346*bea7b932SAlexander Leidinger 
347*bea7b932SAlexander Leidinger 	return (atf_no_error());
348*bea7b932SAlexander Leidinger }
349