1*17fca802SAlexander Leidinger /*-
2*17fca802SAlexander Leidinger * SPDX-License-Identifier: BSD-2-Clause
3*17fca802SAlexander Leidinger *
4*17fca802SAlexander Leidinger * Copyright (c) 2026 Alexander Leidinger <netchild@FreeBSD.org>
5*17fca802SAlexander Leidinger *
6*17fca802SAlexander Leidinger * Redistribution and use in source and binary forms, with or without
7*17fca802SAlexander Leidinger * modification, are permitted provided that the following conditions
8*17fca802SAlexander Leidinger * are met:
9*17fca802SAlexander Leidinger * 1. Redistributions of source code must retain the above copyright
10*17fca802SAlexander Leidinger * notice, this list of conditions and the following disclaimer.
11*17fca802SAlexander Leidinger * 2. Redistributions in binary form must reproduce the above copyright
12*17fca802SAlexander Leidinger * notice, this list of conditions and the following disclaimer in the
13*17fca802SAlexander Leidinger * documentation and/or other advertising materials provided with the
14*17fca802SAlexander Leidinger * distribution.
15*17fca802SAlexander Leidinger *
16*17fca802SAlexander Leidinger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*17fca802SAlexander Leidinger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*17fca802SAlexander Leidinger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*17fca802SAlexander Leidinger * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*17fca802SAlexander Leidinger * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*17fca802SAlexander Leidinger * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*17fca802SAlexander Leidinger * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*17fca802SAlexander Leidinger * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*17fca802SAlexander Leidinger * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*17fca802SAlexander Leidinger * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*17fca802SAlexander Leidinger * SUCH DAMAGE.
27*17fca802SAlexander Leidinger */
28*17fca802SAlexander Leidinger
29*17fca802SAlexander Leidinger /**
30*17fca802SAlexander Leidinger * @file
31*17fca802SAlexander Leidinger * Tests for hwpmc(4)'s handling of the PMC handle and for the privilege
32*17fca802SAlexander Leidinger * boundaries around the operations that do not take one.
33*17fca802SAlexander Leidinger *
34*17fca802SAlexander Leidinger * A pmc_id_t packs CPU, mode, class and row index into one 32-bit word,
35*17fca802SAlexander Leidinger * which the kernel hands to userland and accepts back on eleven
36*17fca802SAlexander Leidinger * operations. The cases pass ids that were never allocated, ids that were
37*17fca802SAlexander Leidinger * released, and ids belonging to a different process, and require each
38*17fca802SAlexander Leidinger * operation to refuse cleanly rather than act on a wrong PMC.
39*17fca802SAlexander Leidinger *
40*17fca802SAlexander Leidinger * Two properties are asserted because a refactor could break either with
41*17fca802SAlexander Leidinger * every existing test still passing: the CPU field is 12 bits, so a caller
42*17fca802SAlexander Leidinger * may name CPU 4095 on a machine that has four; and ids are unique per
43*17fca802SAlexander Leidinger * owner rather than globally, so two processes are given the same numeric
44*17fca802SAlexander Leidinger * id and only the per-owner lookup keeps them apart.
45*17fca802SAlexander Leidinger *
46*17fca802SAlexander Leidinger * The PMCs are SOFT-class, so nothing here needs a hardware counter. The
47*17fca802SAlexander Leidinger * privilege cases need an unprivileged subject - as root the checks under
48*17fca802SAlexander Leidinger * test are never reached - hence require.user.
49*17fca802SAlexander Leidinger */
50*17fca802SAlexander Leidinger
51*17fca802SAlexander Leidinger #include <sys/param.h>
52*17fca802SAlexander Leidinger #include <sys/stat.h>
53*17fca802SAlexander Leidinger #include <sys/sysctl.h>
54*17fca802SAlexander Leidinger #include <sys/wait.h>
55*17fca802SAlexander Leidinger
56*17fca802SAlexander Leidinger #include <errno.h>
57*17fca802SAlexander Leidinger #include <pmc.h>
58*17fca802SAlexander Leidinger #include <signal.h>
59*17fca802SAlexander Leidinger #include <stdint.h>
60*17fca802SAlexander Leidinger #include <stdio.h>
61*17fca802SAlexander Leidinger #include <stdlib.h>
62*17fca802SAlexander Leidinger #include <string.h>
63*17fca802SAlexander Leidinger #include <unistd.h>
64*17fca802SAlexander Leidinger
65*17fca802SAlexander Leidinger #include <atf-c.h>
66*17fca802SAlexander Leidinger
67*17fca802SAlexander Leidinger /**
68*17fca802SAlexander Leidinger * @internal
69*17fca802SAlexander Leidinger * The operations that take a pmc_id_t and reach the kernel with it.
70*17fca802SAlexander Leidinger * pmc_width() is deliberately absent: it answers from a userland copy of
71*17fca802SAlexander Leidinger * the CPU info and never enters the driver.
72*17fca802SAlexander Leidinger */
op_attach(pmc_id_t id)73*17fca802SAlexander Leidinger static int op_attach(pmc_id_t id) { return (pmc_attach(id, getpid())); }
op_detach(pmc_id_t id)74*17fca802SAlexander Leidinger static int op_detach(pmc_id_t id) { return (pmc_detach(id, getpid())); }
op_start(pmc_id_t id)75*17fca802SAlexander Leidinger static int op_start(pmc_id_t id) { return (pmc_start(id)); }
op_stop(pmc_id_t id)76*17fca802SAlexander Leidinger static int op_stop(pmc_id_t id) { return (pmc_stop(id)); }
op_write(pmc_id_t id)77*17fca802SAlexander Leidinger static int op_write(pmc_id_t id) { return (pmc_write(id, 0)); }
op_set(pmc_id_t id)78*17fca802SAlexander Leidinger static int op_set(pmc_id_t id) { return (pmc_set(id, 0)); }
op_release(pmc_id_t id)79*17fca802SAlexander Leidinger static int op_release(pmc_id_t id) { return (pmc_release(id)); }
80*17fca802SAlexander Leidinger
81*17fca802SAlexander Leidinger static int
op_read(pmc_id_t id)82*17fca802SAlexander Leidinger op_read(pmc_id_t id)
83*17fca802SAlexander Leidinger {
84*17fca802SAlexander Leidinger pmc_value_t v;
85*17fca802SAlexander Leidinger
86*17fca802SAlexander Leidinger return (pmc_read(id, &v));
87*17fca802SAlexander Leidinger }
88*17fca802SAlexander Leidinger
89*17fca802SAlexander Leidinger static int
op_rw(pmc_id_t id)90*17fca802SAlexander Leidinger op_rw(pmc_id_t id)
91*17fca802SAlexander Leidinger {
92*17fca802SAlexander Leidinger pmc_value_t v;
93*17fca802SAlexander Leidinger
94*17fca802SAlexander Leidinger return (pmc_rw(id, 0, &v));
95*17fca802SAlexander Leidinger }
96*17fca802SAlexander Leidinger
97*17fca802SAlexander Leidinger static int
op_caps(pmc_id_t id)98*17fca802SAlexander Leidinger op_caps(pmc_id_t id)
99*17fca802SAlexander Leidinger {
100*17fca802SAlexander Leidinger uint32_t c;
101*17fca802SAlexander Leidinger
102*17fca802SAlexander Leidinger return (pmc_capabilities(id, &c));
103*17fca802SAlexander Leidinger }
104*17fca802SAlexander Leidinger
105*17fca802SAlexander Leidinger static int
op_getmsr(pmc_id_t id)106*17fca802SAlexander Leidinger op_getmsr(pmc_id_t id)
107*17fca802SAlexander Leidinger {
108*17fca802SAlexander Leidinger uint32_t msr;
109*17fca802SAlexander Leidinger
110*17fca802SAlexander Leidinger return (pmc_get_msr(id, &msr));
111*17fca802SAlexander Leidinger }
112*17fca802SAlexander Leidinger
113*17fca802SAlexander Leidinger static const struct {
114*17fca802SAlexander Leidinger const char *name;
115*17fca802SAlexander Leidinger int (*fn)(pmc_id_t);
116*17fca802SAlexander Leidinger } id_ops[] = {
117*17fca802SAlexander Leidinger { "attach", op_attach },
118*17fca802SAlexander Leidinger { "detach", op_detach },
119*17fca802SAlexander Leidinger { "start", op_start },
120*17fca802SAlexander Leidinger { "stop", op_stop },
121*17fca802SAlexander Leidinger { "read", op_read },
122*17fca802SAlexander Leidinger { "write", op_write },
123*17fca802SAlexander Leidinger { "rw", op_rw },
124*17fca802SAlexander Leidinger { "set", op_set },
125*17fca802SAlexander Leidinger { "caps", op_caps },
126*17fca802SAlexander Leidinger { "getmsr", op_getmsr },
127*17fca802SAlexander Leidinger { "release", op_release },
128*17fca802SAlexander Leidinger };
129*17fca802SAlexander Leidinger
130*17fca802SAlexander Leidinger static void
require_hwpmc(void)131*17fca802SAlexander Leidinger require_hwpmc(void)
132*17fca802SAlexander Leidinger {
133*17fca802SAlexander Leidinger
134*17fca802SAlexander Leidinger if (pmc_init() != 0)
135*17fca802SAlexander Leidinger atf_tc_skip("hwpmc(4) is not available");
136*17fca802SAlexander Leidinger }
137*17fca802SAlexander Leidinger
138*17fca802SAlexander Leidinger static void
require_unprivileged(void)139*17fca802SAlexander Leidinger require_unprivileged(void)
140*17fca802SAlexander Leidinger {
141*17fca802SAlexander Leidinger
142*17fca802SAlexander Leidinger if (geteuid() == 0)
143*17fca802SAlexander Leidinger atf_tc_skip("the subject must be unprivileged: root passes "
144*17fca802SAlexander Leidinger "the check under test");
145*17fca802SAlexander Leidinger }
146*17fca802SAlexander Leidinger
147*17fca802SAlexander Leidinger /**
148*17fca802SAlexander Leidinger * @internal
149*17fca802SAlexander Leidinger * Allocate the first SOFT event this mode accepts. Every SOFT event takes
150*17fca802SAlexander Leidinger * the same allocate/attach/detach/release path, so which one it is does not
151*17fca802SAlexander Leidinger * matter; that one exists at all is what the case needs.
152*17fca802SAlexander Leidinger */
153*17fca802SAlexander Leidinger static pmc_id_t
allocate_soft_pmc(enum pmc_mode mode)154*17fca802SAlexander Leidinger allocate_soft_pmc(enum pmc_mode mode)
155*17fca802SAlexander Leidinger {
156*17fca802SAlexander Leidinger const char **names;
157*17fca802SAlexander Leidinger char spec[128];
158*17fca802SAlexander Leidinger pmc_id_t id;
159*17fca802SAlexander Leidinger int nnames, i;
160*17fca802SAlexander Leidinger
161*17fca802SAlexander Leidinger if (pmc_event_names_of_class(PMC_CLASS_SOFT, &names, &nnames) != 0)
162*17fca802SAlexander Leidinger return (PMC_ID_INVALID);
163*17fca802SAlexander Leidinger for (i = 0; i < nnames; i++) {
164*17fca802SAlexander Leidinger (void)snprintf(spec, sizeof(spec), "SOFT-%s", names[i]);
165*17fca802SAlexander Leidinger if (pmc_allocate(spec, mode, 0, PMC_CPU_ANY, &id, 0) == 0)
166*17fca802SAlexander Leidinger return (id);
167*17fca802SAlexander Leidinger }
168*17fca802SAlexander Leidinger return (PMC_ID_INVALID);
169*17fca802SAlexander Leidinger }
170*17fca802SAlexander Leidinger
171*17fca802SAlexander Leidinger static pmc_id_t
require_soft_pmc(enum pmc_mode mode)172*17fca802SAlexander Leidinger require_soft_pmc(enum pmc_mode mode)
173*17fca802SAlexander Leidinger {
174*17fca802SAlexander Leidinger pmc_id_t id;
175*17fca802SAlexander Leidinger
176*17fca802SAlexander Leidinger if ((id = allocate_soft_pmc(mode)) == PMC_ID_INVALID)
177*17fca802SAlexander Leidinger atf_tc_skip("no SOFT-class PMC is allocatable");
178*17fca802SAlexander Leidinger return (id);
179*17fca802SAlexander Leidinger }
180*17fca802SAlexander Leidinger
181*17fca802SAlexander Leidinger /**
182*17fca802SAlexander Leidinger * @internal
183*17fca802SAlexander Leidinger * Every id-taking operation must refuse this id. Checks rather than
184*17fca802SAlexander Leidinger * requires, so one operation that wrongly accepts does not hide the rest.
185*17fca802SAlexander Leidinger */
186*17fca802SAlexander Leidinger static void
check_all_ops_refuse(pmc_id_t id,const char * what)187*17fca802SAlexander Leidinger check_all_ops_refuse(pmc_id_t id, const char *what)
188*17fca802SAlexander Leidinger {
189*17fca802SAlexander Leidinger size_t i;
190*17fca802SAlexander Leidinger
191*17fca802SAlexander Leidinger for (i = 0; i < nitems(id_ops); i++) {
192*17fca802SAlexander Leidinger errno = 0;
193*17fca802SAlexander Leidinger ATF_CHECK_MSG(id_ops[i].fn(id) != 0,
194*17fca802SAlexander Leidinger "pmc_%s() accepted %s id 0x%08x", id_ops[i].name, what,
195*17fca802SAlexander Leidinger id);
196*17fca802SAlexander Leidinger }
197*17fca802SAlexander Leidinger }
198*17fca802SAlexander Leidinger
199*17fca802SAlexander Leidinger ATF_TC_WITHOUT_HEAD(never_allocated_id);
ATF_TC_BODY(never_allocated_id,tc)200*17fca802SAlexander Leidinger ATF_TC_BODY(never_allocated_id, tc)
201*17fca802SAlexander Leidinger {
202*17fca802SAlexander Leidinger pmc_id_t forged[6];
203*17fca802SAlexander Leidinger size_t i;
204*17fca802SAlexander Leidinger int ncpu;
205*17fca802SAlexander Leidinger
206*17fca802SAlexander Leidinger require_hwpmc();
207*17fca802SAlexander Leidinger ncpu = pmc_ncpu();
208*17fca802SAlexander Leidinger ATF_REQUIRE(ncpu > 0);
209*17fca802SAlexander Leidinger
210*17fca802SAlexander Leidinger /*
211*17fca802SAlexander Leidinger * Nothing has been allocated in this process, so every one of these
212*17fca802SAlexander Leidinger * has to be refused - including the two that are well-formed apart
213*17fca802SAlexander Leidinger * from naming a CPU that does not exist.
214*17fca802SAlexander Leidinger */
215*17fca802SAlexander Leidinger forged[0] = PMC_ID_INVALID;
216*17fca802SAlexander Leidinger forged[1] = PMC_ID_MAKE_ID(0xFFF, PMC_MODE_TC, PMC_CLASS_SOFT, 0);
217*17fca802SAlexander Leidinger forged[2] = PMC_ID_MAKE_ID(PMC_CPU_ANY, PMC_MODE_TC, PMC_CLASS_SOFT,
218*17fca802SAlexander Leidinger 0xFF);
219*17fca802SAlexander Leidinger forged[3] = PMC_ID_MAKE_ID(ncpu, PMC_MODE_TC, PMC_CLASS_SOFT, 0);
220*17fca802SAlexander Leidinger forged[4] = 0;
221*17fca802SAlexander Leidinger forged[5] = PMC_ID_MAKE_ID(PMC_CPU_ANY, 0xF, 0xFF, 0);
222*17fca802SAlexander Leidinger
223*17fca802SAlexander Leidinger for (i = 0; i < nitems(forged); i++)
224*17fca802SAlexander Leidinger check_all_ops_refuse(forged[i], "never-allocated");
225*17fca802SAlexander Leidinger }
226*17fca802SAlexander Leidinger
227*17fca802SAlexander Leidinger ATF_TC_WITHOUT_HEAD(released_id);
ATF_TC_BODY(released_id,tc)228*17fca802SAlexander Leidinger ATF_TC_BODY(released_id, tc)
229*17fca802SAlexander Leidinger {
230*17fca802SAlexander Leidinger pmc_id_t id;
231*17fca802SAlexander Leidinger
232*17fca802SAlexander Leidinger require_hwpmc();
233*17fca802SAlexander Leidinger id = require_soft_pmc(PMC_MODE_TC);
234*17fca802SAlexander Leidinger
235*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s",
236*17fca802SAlexander Leidinger strerror(errno));
237*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_start(id) == 0, "pmc_start: %s", strerror(errno));
238*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_stop(id) == 0, "pmc_stop: %s", strerror(errno));
239*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_detach(id, getpid()) == 0, "pmc_detach: %s",
240*17fca802SAlexander Leidinger strerror(errno));
241*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_release(id) == 0, "pmc_release: %s",
242*17fca802SAlexander Leidinger strerror(errno));
243*17fca802SAlexander Leidinger
244*17fca802SAlexander Leidinger /* The same value is now stale. Reusing it is the double-free shape. */
245*17fca802SAlexander Leidinger check_all_ops_refuse(id, "released");
246*17fca802SAlexander Leidinger }
247*17fca802SAlexander Leidinger
248*17fca802SAlexander Leidinger ATF_TC_WITHOUT_HEAD(another_owners_id);
ATF_TC_BODY(another_owners_id,tc)249*17fca802SAlexander Leidinger ATF_TC_BODY(another_owners_id, tc)
250*17fca802SAlexander Leidinger {
251*17fca802SAlexander Leidinger pmc_id_t id, mine;
252*17fca802SAlexander Leidinger pmc_value_t v;
253*17fca802SAlexander Leidinger pid_t child;
254*17fca802SAlexander Leidinger ssize_t n;
255*17fca802SAlexander Leidinger int down[2], up[2], status;
256*17fca802SAlexander Leidinger char token;
257*17fca802SAlexander Leidinger
258*17fca802SAlexander Leidinger require_hwpmc();
259*17fca802SAlexander Leidinger ATF_REQUIRE(pipe(down) == 0);
260*17fca802SAlexander Leidinger ATF_REQUIRE(pipe(up) == 0);
261*17fca802SAlexander Leidinger ATF_REQUIRE((child = fork()) >= 0);
262*17fca802SAlexander Leidinger
263*17fca802SAlexander Leidinger if (child == 0) {
264*17fca802SAlexander Leidinger pmc_value_t cv;
265*17fca802SAlexander Leidinger pmc_id_t cid;
266*17fca802SAlexander Leidinger
267*17fca802SAlexander Leidinger (void)close(down[1]);
268*17fca802SAlexander Leidinger (void)close(up[0]);
269*17fca802SAlexander Leidinger if (pmc_init() != 0)
270*17fca802SAlexander Leidinger _exit(2);
271*17fca802SAlexander Leidinger if ((cid = allocate_soft_pmc(PMC_MODE_TC)) == PMC_ID_INVALID)
272*17fca802SAlexander Leidinger _exit(3);
273*17fca802SAlexander Leidinger if (pmc_attach(cid, getpid()) != 0 || pmc_start(cid) != 0)
274*17fca802SAlexander Leidinger _exit(4);
275*17fca802SAlexander Leidinger if (write(up[1], &cid, sizeof(cid)) != (ssize_t)sizeof(cid))
276*17fca802SAlexander Leidinger _exit(5);
277*17fca802SAlexander Leidinger if (read(down[0], &token, 1) != 1)
278*17fca802SAlexander Leidinger _exit(6);
279*17fca802SAlexander Leidinger /* Untouched by anything the parent did? */
280*17fca802SAlexander Leidinger _exit(pmc_read(cid, &cv) == 0 ? 0 : 7);
281*17fca802SAlexander Leidinger }
282*17fca802SAlexander Leidinger (void)close(down[0]);
283*17fca802SAlexander Leidinger (void)close(up[1]);
284*17fca802SAlexander Leidinger
285*17fca802SAlexander Leidinger n = read(up[0], &id, sizeof(id));
286*17fca802SAlexander Leidinger if (n != (ssize_t)sizeof(id)) {
287*17fca802SAlexander Leidinger (void)kill(child, SIGKILL);
288*17fca802SAlexander Leidinger (void)waitpid(child, &status, 0);
289*17fca802SAlexander Leidinger atf_tc_skip("the child could not allocate a SOFT-class PMC");
290*17fca802SAlexander Leidinger }
291*17fca802SAlexander Leidinger
292*17fca802SAlexander Leidinger /* We own nothing, so the child's id must not resolve for us. */
293*17fca802SAlexander Leidinger check_all_ops_refuse(id, "another owner's");
294*17fca802SAlexander Leidinger
295*17fca802SAlexander Leidinger /*
296*17fca802SAlexander Leidinger * Now hold one of our own. Ids are per owner, so ours is very
297*17fca802SAlexander Leidinger * likely the same number; operating on it must still reach only
298*17fca802SAlexander Leidinger * ours, which the child's exit status confirms.
299*17fca802SAlexander Leidinger */
300*17fca802SAlexander Leidinger mine = allocate_soft_pmc(PMC_MODE_TC);
301*17fca802SAlexander Leidinger if (mine != PMC_ID_INVALID) {
302*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_attach(mine, getpid()) == 0, "pmc_attach: %s",
303*17fca802SAlexander Leidinger strerror(errno));
304*17fca802SAlexander Leidinger ATF_CHECK(pmc_read(mine, &v) == 0);
305*17fca802SAlexander Leidinger ATF_CHECK(pmc_detach(mine, getpid()) == 0);
306*17fca802SAlexander Leidinger ATF_CHECK(pmc_release(mine) == 0);
307*17fca802SAlexander Leidinger }
308*17fca802SAlexander Leidinger
309*17fca802SAlexander Leidinger ATF_REQUIRE(write(down[1], "g", 1) == 1);
310*17fca802SAlexander Leidinger (void)close(down[1]);
311*17fca802SAlexander Leidinger ATF_REQUIRE(waitpid(child, &status, 0) == child);
312*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
313*17fca802SAlexander Leidinger "the child's own PMC did not survive our use of its id "
314*17fca802SAlexander Leidinger "(child exit %d)", WIFEXITED(status) ? WEXITSTATUS(status) : -1);
315*17fca802SAlexander Leidinger }
316*17fca802SAlexander Leidinger
317*17fca802SAlexander Leidinger ATF_TC_WITHOUT_HEAD(read_write_before_start);
ATF_TC_BODY(read_write_before_start,tc)318*17fca802SAlexander Leidinger ATF_TC_BODY(read_write_before_start, tc)
319*17fca802SAlexander Leidinger {
320*17fca802SAlexander Leidinger pmc_value_t old, v;
321*17fca802SAlexander Leidinger pmc_id_t id;
322*17fca802SAlexander Leidinger
323*17fca802SAlexander Leidinger require_hwpmc();
324*17fca802SAlexander Leidinger id = require_soft_pmc(PMC_MODE_TC);
325*17fca802SAlexander Leidinger ATF_REQUIRE_MSG(pmc_attach(id, getpid()) == 0, "pmc_attach: %s",
326*17fca802SAlexander Leidinger strerror(errno));
327*17fca802SAlexander Leidinger
328*17fca802SAlexander Leidinger /* Allocated and attached, never started: a read must still work. */
329*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_read(id, &v) == 0, "pmc_read before start: %s",
330*17fca802SAlexander Leidinger strerror(errno));
331*17fca802SAlexander Leidinger
332*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_write(id, 42) == 0, "pmc_write before start: %s",
333*17fca802SAlexander Leidinger strerror(errno));
334*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_read(id, &v) == 0, "pmc_read: %s", strerror(errno));
335*17fca802SAlexander Leidinger ATF_CHECK_MSG(v == 42, "wrote 42, read back %ju", (uintmax_t)v);
336*17fca802SAlexander Leidinger
337*17fca802SAlexander Leidinger ATF_CHECK(pmc_rw(id, 7, &old) == 0);
338*17fca802SAlexander Leidinger ATF_CHECK_MSG(old == 42, "pmc_rw returned %ju, expected the 42 "
339*17fca802SAlexander Leidinger "written before it", (uintmax_t)old);
340*17fca802SAlexander Leidinger
341*17fca802SAlexander Leidinger ATF_REQUIRE(pmc_start(id) == 0);
342*17fca802SAlexander Leidinger ATF_REQUIRE(pmc_stop(id) == 0);
343*17fca802SAlexander Leidinger ATF_CHECK(pmc_detach(id, getpid()) == 0);
344*17fca802SAlexander Leidinger ATF_CHECK(pmc_release(id) == 0);
345*17fca802SAlexander Leidinger }
346*17fca802SAlexander Leidinger
347*17fca802SAlexander Leidinger ATF_TC(pmcadmin_requires_privilege);
ATF_TC_HEAD(pmcadmin_requires_privilege,tc)348*17fca802SAlexander Leidinger ATF_TC_HEAD(pmcadmin_requires_privilege, tc)
349*17fca802SAlexander Leidinger {
350*17fca802SAlexander Leidinger
351*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "descr",
352*17fca802SAlexander Leidinger "PMC_OP_PMCADMIN is refused to an unprivileged caller");
353*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "require.user", "unprivileged");
354*17fca802SAlexander Leidinger }
ATF_TC_BODY(pmcadmin_requires_privilege,tc)355*17fca802SAlexander Leidinger ATF_TC_BODY(pmcadmin_requires_privilege, tc)
356*17fca802SAlexander Leidinger {
357*17fca802SAlexander Leidinger
358*17fca802SAlexander Leidinger require_hwpmc();
359*17fca802SAlexander Leidinger require_unprivileged();
360*17fca802SAlexander Leidinger
361*17fca802SAlexander Leidinger errno = 0;
362*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_disable(0, 0) != 0,
363*17fca802SAlexander Leidinger "an unprivileged process disabled a PMC row");
364*17fca802SAlexander Leidinger ATF_CHECK_MSG(errno == EPERM, "pmc_disable: expected EPERM, got %s",
365*17fca802SAlexander Leidinger strerror(errno));
366*17fca802SAlexander Leidinger
367*17fca802SAlexander Leidinger errno = 0;
368*17fca802SAlexander Leidinger ATF_CHECK_MSG(pmc_enable(0, 0) != 0,
369*17fca802SAlexander Leidinger "an unprivileged process enabled a PMC row");
370*17fca802SAlexander Leidinger ATF_CHECK_MSG(errno == EPERM, "pmc_enable: expected EPERM, got %s",
371*17fca802SAlexander Leidinger strerror(errno));
372*17fca802SAlexander Leidinger }
373*17fca802SAlexander Leidinger
374*17fca802SAlexander Leidinger ATF_TC(system_mode_requires_privilege);
ATF_TC_HEAD(system_mode_requires_privilege,tc)375*17fca802SAlexander Leidinger ATF_TC_HEAD(system_mode_requires_privilege, tc)
376*17fca802SAlexander Leidinger {
377*17fca802SAlexander Leidinger
378*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "descr",
379*17fca802SAlexander Leidinger "a system-wide PMC is refused to an unprivileged caller");
380*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "require.user", "unprivileged");
381*17fca802SAlexander Leidinger }
ATF_TC_BODY(system_mode_requires_privilege,tc)382*17fca802SAlexander Leidinger ATF_TC_BODY(system_mode_requires_privilege, tc)
383*17fca802SAlexander Leidinger {
384*17fca802SAlexander Leidinger const char **names;
385*17fca802SAlexander Leidinger char spec[128];
386*17fca802SAlexander Leidinger pmc_id_t id;
387*17fca802SAlexander Leidinger size_t len;
388*17fca802SAlexander Leidinger int nnames, i, unpriv;
389*17fca802SAlexander Leidinger
390*17fca802SAlexander Leidinger require_hwpmc();
391*17fca802SAlexander Leidinger require_unprivileged();
392*17fca802SAlexander Leidinger
393*17fca802SAlexander Leidinger len = sizeof(unpriv);
394*17fca802SAlexander Leidinger if (sysctlbyname("security.bsd.unprivileged_syspmcs", &unpriv, &len,
395*17fca802SAlexander Leidinger NULL, 0) != 0)
396*17fca802SAlexander Leidinger atf_tc_skip("security.bsd.unprivileged_syspmcs is unreadable");
397*17fca802SAlexander Leidinger if (unpriv != 0)
398*17fca802SAlexander Leidinger atf_tc_skip("security.bsd.unprivileged_syspmcs is set: "
399*17fca802SAlexander Leidinger "unprivileged system-wide PMCs are permitted here");
400*17fca802SAlexander Leidinger
401*17fca802SAlexander Leidinger if (pmc_event_names_of_class(PMC_CLASS_SOFT, &names, &nnames) != 0 ||
402*17fca802SAlexander Leidinger nnames == 0)
403*17fca802SAlexander Leidinger atf_tc_skip("no SOFT-class events");
404*17fca802SAlexander Leidinger
405*17fca802SAlexander Leidinger /*
406*17fca802SAlexander Leidinger * A system-mode PMC must name a real CPU: PMC_CPU_ANY is rejected
407*17fca802SAlexander Leidinger * before the privilege check is reached, which would make this case
408*17fca802SAlexander Leidinger * pass for the wrong reason.
409*17fca802SAlexander Leidinger */
410*17fca802SAlexander Leidinger for (i = 0; i < nnames; i++) {
411*17fca802SAlexander Leidinger (void)snprintf(spec, sizeof(spec), "SOFT-%s", names[i]);
412*17fca802SAlexander Leidinger errno = 0;
413*17fca802SAlexander Leidinger if (pmc_allocate(spec, PMC_MODE_SC, 0, 0, &id, 0) == 0) {
414*17fca802SAlexander Leidinger (void)pmc_release(id);
415*17fca802SAlexander Leidinger atf_tc_fail("an unprivileged process allocated a "
416*17fca802SAlexander Leidinger "system-wide PMC (%s)", spec);
417*17fca802SAlexander Leidinger }
418*17fca802SAlexander Leidinger ATF_CHECK_MSG(errno == EPERM,
419*17fca802SAlexander Leidinger "%s: expected EPERM, got %s", spec, strerror(errno));
420*17fca802SAlexander Leidinger }
421*17fca802SAlexander Leidinger }
422*17fca802SAlexander Leidinger
423*17fca802SAlexander Leidinger /**
424*17fca802SAlexander Leidinger * @internal
425*17fca802SAlexander Leidinger * A target that has exec'ed a set-id binary carries P_SUGID, which
426*17fca802SAlexander Leidinger * p_candebug() refuses to an unprivileged subject - the same rule
427*17fca802SAlexander Leidinger * PMC_OP_PMCATTACH inherits. wall(1) is set-gid and reads its message from
428*17fca802SAlexander Leidinger * stdin, so it stays alive on a pipe long enough to be attached to.
429*17fca802SAlexander Leidinger */
430*17fca802SAlexander Leidinger ATF_TC(attach_to_sugid_target);
ATF_TC_HEAD(attach_to_sugid_target,tc)431*17fca802SAlexander Leidinger ATF_TC_HEAD(attach_to_sugid_target, tc)
432*17fca802SAlexander Leidinger {
433*17fca802SAlexander Leidinger
434*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "descr",
435*17fca802SAlexander Leidinger "attaching a PMC to a process that has exec'ed a set-id binary "
436*17fca802SAlexander Leidinger "is refused to an unprivileged owner");
437*17fca802SAlexander Leidinger atf_tc_set_md_var(tc, "require.user", "unprivileged");
438*17fca802SAlexander Leidinger }
ATF_TC_BODY(attach_to_sugid_target,tc)439*17fca802SAlexander Leidinger ATF_TC_BODY(attach_to_sugid_target, tc)
440*17fca802SAlexander Leidinger {
441*17fca802SAlexander Leidinger static const char *const argv[] = { "wall", NULL };
442*17fca802SAlexander Leidinger static const char path[] = "/usr/bin/wall";
443*17fca802SAlexander Leidinger struct stat sb;
444*17fca802SAlexander Leidinger pmc_id_t id;
445*17fca802SAlexander Leidinger pid_t target;
446*17fca802SAlexander Leidinger int gopipe[2], inpipe[2], status, rc;
447*17fca802SAlexander Leidinger char token;
448*17fca802SAlexander Leidinger
449*17fca802SAlexander Leidinger require_hwpmc();
450*17fca802SAlexander Leidinger require_unprivileged();
451*17fca802SAlexander Leidinger
452*17fca802SAlexander Leidinger if (stat(path, &sb) != 0 || (sb.st_mode & S_ISGID) == 0)
453*17fca802SAlexander Leidinger atf_tc_skip("%s is not set-gid here", path);
454*17fca802SAlexander Leidinger if (getegid() == sb.st_gid)
455*17fca802SAlexander Leidinger atf_tc_skip("the subject is already in %s's group, so it may "
456*17fca802SAlexander Leidinger "trace the target", path);
457*17fca802SAlexander Leidinger
458*17fca802SAlexander Leidinger ATF_REQUIRE(pipe(gopipe) == 0);
459*17fca802SAlexander Leidinger ATF_REQUIRE(pipe(inpipe) == 0);
460*17fca802SAlexander Leidinger ATF_REQUIRE((target = fork()) >= 0);
461*17fca802SAlexander Leidinger if (target == 0) {
462*17fca802SAlexander Leidinger (void)close(gopipe[1]);
463*17fca802SAlexander Leidinger (void)dup2(inpipe[0], STDIN_FILENO);
464*17fca802SAlexander Leidinger (void)close(inpipe[1]);
465*17fca802SAlexander Leidinger if (read(gopipe[0], &token, 1) != 1)
466*17fca802SAlexander Leidinger _exit(1);
467*17fca802SAlexander Leidinger (void)execv(path, __DECONST(char **, argv));
468*17fca802SAlexander Leidinger _exit(1);
469*17fca802SAlexander Leidinger }
470*17fca802SAlexander Leidinger (void)close(gopipe[0]);
471*17fca802SAlexander Leidinger (void)close(inpipe[0]);
472*17fca802SAlexander Leidinger
473*17fca802SAlexander Leidinger id = require_soft_pmc(PMC_MODE_TC);
474*17fca802SAlexander Leidinger
475*17fca802SAlexander Leidinger /* Let it exec, then give it time to get there. */
476*17fca802SAlexander Leidinger ATF_REQUIRE(write(gopipe[1], "g", 1) == 1);
477*17fca802SAlexander Leidinger (void)close(gopipe[1]);
478*17fca802SAlexander Leidinger (void)usleep(400000);
479*17fca802SAlexander Leidinger
480*17fca802SAlexander Leidinger errno = 0;
481*17fca802SAlexander Leidinger rc = pmc_attach(id, target);
482*17fca802SAlexander Leidinger if (rc == 0)
483*17fca802SAlexander Leidinger (void)pmc_detach(id, target);
484*17fca802SAlexander Leidinger (void)kill(target, SIGKILL);
485*17fca802SAlexander Leidinger (void)close(inpipe[1]);
486*17fca802SAlexander Leidinger (void)waitpid(target, &status, 0);
487*17fca802SAlexander Leidinger (void)pmc_release(id);
488*17fca802SAlexander Leidinger
489*17fca802SAlexander Leidinger ATF_CHECK_MSG(rc != 0,
490*17fca802SAlexander Leidinger "an unprivileged owner attached a PMC to a set-id target");
491*17fca802SAlexander Leidinger ATF_CHECK_MSG(errno == EPERM, "pmc_attach: expected EPERM, got %s",
492*17fca802SAlexander Leidinger strerror(errno));
493*17fca802SAlexander Leidinger }
494*17fca802SAlexander Leidinger
ATF_TP_ADD_TCS(tp)495*17fca802SAlexander Leidinger ATF_TP_ADD_TCS(tp)
496*17fca802SAlexander Leidinger {
497*17fca802SAlexander Leidinger
498*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, never_allocated_id);
499*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, released_id);
500*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, another_owners_id);
501*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, read_write_before_start);
502*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, pmcadmin_requires_privilege);
503*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, system_mode_requires_privilege);
504*17fca802SAlexander Leidinger ATF_TP_ADD_TC(tp, attach_to_sugid_target);
505*17fca802SAlexander Leidinger
506*17fca802SAlexander Leidinger return (atf_no_error());
507*17fca802SAlexander Leidinger }
508