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