1 // Copyright 2023 (c) Apple Inc. All rights reserved.
2
3 #include <darwintest.h>
4 #include <darwintest_utils.h>
5 #include <dirent.h>
6 #include <kperf/kpc.h>
7 #include <kperfdata/kpep.h>
8 #include <stdarg.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <sys/guarded.h>
12 #include <sys/ioctl.h>
13 #include <sys/monotonic.h>
14
15 #include "test_utils.h"
16
17 #if __arm64__
18 #define HAS_CPC_SECURITY true
19 #else // __arm64__
20 #define HAS_CPC_SECURITY false
21 #endif // !__arm64__
22
23 #define _T_META_REQUIRES_CPC_SUPPORT \
24 T_META_REQUIRES_SYSCTL_EQ("kern.monotonic.supported", "1")
25
26 T_GLOBAL_META(
27 T_META_NAMESPACE("xnu.cpc"),
28 T_META_RADAR_COMPONENT_NAME("xnu"),
29 T_META_RADAR_COMPONENT_VERSION("cpu counters"),
30 T_META_OWNER("mwidmann"),
31 T_META_CHECK_LEAKS(false),
32 XNU_T_META_SOC_SPECIFIC,
33 T_META_ENABLED(HAS_CPC_SECURITY),
34 _T_META_REQUIRES_CPC_SUPPORT);
35
36 // Several of these tests have two variants to support running on development and release kernels.
37 // Tests prefixed with `secure_` put the development kernel into a secure CPC mode while tests prefixed with `release_` can run on the RELEASE build variant.
38
39 // Metadata for running on a development kernel in CPC secure mode.
40 //
41 // This should require kern.development to be 1 with XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL,
42 // but libdarwintest has a bug (rdar://111297938) preventing that.
43 // In the meantime, manually check in the test whether the kernel is DEVELOPMENT.
44 #define _T_META_CPC_SECURE_ON_DEV T_META_SYSCTL_INT("kern.cpc.secure=1")
45
46 static void
_skip_unless_development(void)47 _skip_unless_development(void)
48 {
49 unsigned int dev = 0;
50 size_t dev_size = sizeof(dev);
51 int ret = sysctlbyname("kern.development", &dev, &dev_size, NULL, 0);
52 if (ret < 0 || dev) {
53 T_SKIP("test must run on DEVELOPMENT kernel");
54 }
55 }
56
57 static void
_assert_kpep_ok(int kpep_err,const char * fmt,...)58 _assert_kpep_ok(int kpep_err, const char *fmt, ...)
59 {
60 char msg[1024] = "";
61 va_list args;
62 va_start(args, fmt);
63 vsnprintf(msg, sizeof(msg), fmt, args);
64 va_end(args);
65 T_QUIET;
66 T_ASSERT_EQ(kpep_err, KPEP_ERR_NONE, "%s: %s", msg, kpep_strerror(kpep_err));
67 }
68
69 static void
_skip_for_db(const char * kind,int kpep_err)70 _skip_for_db(const char *kind, int kpep_err)
71 {
72 const char * const public_kpep_path = "/usr/share/kpep";
73 const char * const internal_kpep_path = "/usr/local/share/kpep";
74 const char * const paths[2] = { public_kpep_path, internal_kpep_path, };
75 for (int i = 0; i < 2; i++) {
76 const char * const path = paths[i];
77 T_LOG("contents of %s:", path);
78 DIR *dir = opendir(path);
79 if (dir) {
80 struct dirent *entry = NULL;
81 while ((entry = readdir(dir)) != NULL) {
82 T_LOG(" %s", entry->d_name);
83 }
84 (void)closedir(dir);
85 } else {
86 T_LOG("failed to open directory: %s", strerror(errno));
87 }
88 }
89 int cpu_family = 0;
90 size_t family_size = sizeof(cpu_family);
91 int ret = sysctlbyname("hw.cpufamily", &cpu_family, &family_size, NULL, 0);
92 if (ret != 0) {
93 T_LOG("HW CPU family: 0x%8x", cpu_family);
94 } else {
95 T_LOG("failed to get hw.cpufamily: %s", strerror(errno));
96 }
97 T_SKIP("cannot open %s event database: %s", kind, kpep_strerror(kpep_err));
98 }
99
100 // Check that a secure kernel disallows restricted events.
101
102 static void
check_secure_cpmu(void)103 check_secure_cpmu(void)
104 {
105 kpep_db_t public_db = NULL;
106 int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &public_db);
107 if (ret != KPEP_ERR_NONE) {
108 _skip_for_db("public", ret);
109 }
110 kpep_db_t internal_db = NULL;
111 ret = kpep_db_createx(NULL, KPEP_DB_FLAG_INTERNAL_ONLY, &internal_db);
112 if (ret != KPEP_ERR_NONE) {
113 _skip_for_db("internal", ret);
114 }
115 const char *na = NULL;
116 kpep_db_name(public_db, &na);
117
118 size_t internal_event_count = 0;
119 ret = kpep_db_events_count(internal_db, &internal_event_count);
120 _assert_kpep_ok(ret, "getting internal event count");
121
122 kpep_event_t *internal_events = calloc(internal_event_count,
123 sizeof(internal_events[0]));
124 T_QUIET; T_WITH_ERRNO;
125 T_ASSERT_NOTNULL(internal_events, "allocate space for internal events");
126
127 ret = kpep_db_events(internal_db, internal_events,
128 internal_event_count * sizeof(internal_events[0]));
129 _assert_kpep_ok(ret, "getting internal events");
130
131 kpep_config_t config = NULL;
132 ret = kpep_config_create(internal_db, &config);
133 _assert_kpep_ok(ret, "creating event configuration");
134 ret = kpep_config_force_counters(config);
135 _assert_kpep_ok(ret, "forcing counters with configuration");
136
137 unsigned int tested = 0;
138 unsigned int filtered = 0;
139 unsigned int public_tested = 0;
140 for (size_t i = 0; i < internal_event_count; i++) {
141 kpep_event_t event = internal_events[i];
142 const char *name = NULL;
143 ret = kpep_event_alias(event, &name);
144 if (!name) {
145 ret = kpep_event_name(event, &name);
146 }
147 _assert_kpep_ok(ret, "getting event name");
148 if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
149 T_LOG("skipping non-configurable %s event", name);
150 continue;
151 }
152 bool empty_event = strcmp(name, "NO_EVNT") == 0;
153 if (empty_event) {
154 continue;
155 }
156
157 kpep_event_t public_event = NULL;
158 ret = kpep_db_event(public_db, name, &public_event);
159 bool internal_only = ret == KPEP_ERR_EVENT_NOT_FOUND;
160 ret = kpep_config_add_event(config, &event, 0, NULL);
161 _assert_kpep_ok(ret, "adding event %s to configuration", name);
162
163 ret = kpep_config_apply(config);
164 bool not_permitted = ret == KPEP_ERR_ERRNO && errno == EPERM;
165 if (not_permitted) {
166 if (!internal_only) {
167 T_LOG("failed to configure public event %s", name);
168 }
169 filtered++;
170 } else if (internal_only) {
171 T_FAIL("configured internal-only event %s with secure CPC", name);
172 } else {
173 public_tested++;
174 }
175 ret = kpep_config_remove_event(config, 0);
176 _assert_kpep_ok(ret, "removing event %s from configuration", name);
177 tested++;
178 }
179
180 T_LOG("tested %u internal/public events", tested);
181 T_LOG("correctly permitted to configure %u public events", public_tested);
182 T_LOG("correctly not permitted to configure %u internal-only events",
183 filtered);
184 kpep_config_free(config);
185 kpep_db_free(public_db);
186 kpep_db_free(internal_db);
187 }
188
189 T_DECL(secure_cpmu_event_restrictions, "secured CPMU should be restricted to known events",
190 _T_META_CPC_SECURE_ON_DEV)
191 {
192 _skip_unless_development();
193 check_secure_cpmu();
194 }
195
196 T_DECL(release_cpmu_event_restrictions, "release CPMU should be restricted to known events",
197 XNU_T_META_REQUIRES_RELEASE_KERNEL)
198 {
199 check_secure_cpmu();
200 }
201
202 #define UNCORE_DEV_PATH "/dev/monotonic/uncore"
203 #define UPMU_REF_CYCLES 0x02
204
205 static void
check_secure_upmu(void)206 check_secure_upmu(void)
207 {
208 guardid_t guard;
209 int fd;
210
211 guard = 0xa5adcafe;
212
213 T_SETUPBEGIN;
214
215 fd = guarded_open_np(UNCORE_DEV_PATH, &guard,
216 GUARD_CLOSE | GUARD_DUP | GUARD_WRITE, O_CLOEXEC | O_EXCL);
217 if (fd < 0 && errno == ENOENT) {
218 T_SKIP("uncore counters are unsupported");
219 }
220
221 union monotonic_ctl_add add_ctl = {
222 .in.config.event = UPMU_REF_CYCLES,
223 .in.config.allowed_ctr_mask = 0xffff,
224 };
225
226 T_SETUPEND;
227
228 int ret = ioctl(fd, MT_IOC_ADD, &add_ctl);
229 T_EXPECT_POSIX_FAILURE(ret, EPERM,
230 "should not be allowed to count any events on UPMU");
231 }
232
233 T_DECL(secure_upmu_event_restrictions, "secured UPMU should be restricted to no events",
234 _T_META_CPC_SECURE_ON_DEV)
235 {
236 _skip_unless_development();
237 check_secure_upmu();
238 }
239
240 T_DECL(release_upmu_event_restrictions, "release UPMU should be restricted to no events",
241 XNU_T_META_REQUIRES_RELEASE_KERNEL)
242 {
243 check_secure_upmu();
244 }
245
246 // Check that events which are exposed publicly are allowed to be configured.
247
248 static void
check_event_coverage(kpep_db_flags_t flag,const char * kind)249 check_event_coverage(kpep_db_flags_t flag, const char *kind)
250 {
251 kpep_db_t db = NULL;
252 int ret = kpep_db_createx(NULL, flag, &db);
253 _assert_kpep_ok(ret, "creating %s event database", kind);
254
255 size_t event_count = 0;
256 ret = kpep_db_events_count(db, &event_count);
257 _assert_kpep_ok(ret, "getting %s event count", kind);
258
259 kpep_event_t *events = calloc(event_count, sizeof(events[0]));
260 T_QUIET; T_WITH_ERRNO;
261 T_ASSERT_NOTNULL(events, "allocate space for events");
262
263 ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
264 _assert_kpep_ok(ret, "getting public events");
265
266 kpep_config_t config = NULL;
267 ret = kpep_config_create(db, &config);
268 _assert_kpep_ok(ret, "creating event configuration");
269 ret = kpep_config_force_counters(config);
270 _assert_kpep_ok(ret, "forcing counters with configuration");
271
272 unsigned int tested = 0;
273 for (size_t i = 0; i < event_count; i++) {
274 kpep_event_t event = events[i];
275 const char *name = NULL;
276 ret = kpep_event_name(event, &name);
277 _assert_kpep_ok(ret, "getting event name");
278 if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
279 T_LOG("skipping non-configurable %s event", name);
280 continue;
281 }
282
283 ret = kpep_config_add_event(config, &event, 0, NULL);
284 _assert_kpep_ok(ret, "adding event %s to configuration", name);
285
286 ret = kpep_config_apply(config);
287 if (ret == KPEP_ERR_ERRNO && errno == EPERM) {
288 T_FAIL("failed to configure %s event %s with secure CPC", kind, name);
289 } else {
290 _assert_kpep_ok(ret, "applying configuration with event %s", name);
291 }
292 ret = kpep_config_remove_event(config, 0);
293 _assert_kpep_ok(ret, "removing event %s from configuration", name);
294 tested++;
295 }
296
297 T_LOG("successfully configured %u %s events", tested, kind);
298 kpep_config_free(config);
299 kpep_db_free(db);
300 }
301
302 T_DECL(secure_public_event_coverage, "all public events in kpep should be allowed",
303 _T_META_CPC_SECURE_ON_DEV)
304 {
305 _skip_unless_development();
306 check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
307 }
308
309 T_DECL(release_public_event_coverage, "all public events in kpep should be allowed",
310 XNU_T_META_REQUIRES_RELEASE_KERNEL)
311 {
312 check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
313 }
314
315 // Check for internal development behaviors.
316
317 T_DECL(insecure_cpmu_unrestricted, "insecure CPMU should be unrestricted",
318 XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL, T_META_SYSCTL_INT("kern.cpc.secure=0"))
319 {
320 check_event_coverage(KPEP_DB_FLAG_INTERNAL_ONLY, "internal");
321 }
322