xref: /xnu-12377.81.4/tests/cpu_counters/cpc_security_tests.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
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 	T_META_ASROOT(true),
33 	XNU_T_META_SOC_SPECIFIC,
34 	T_META_ENABLED(HAS_CPC_SECURITY),
35 	_T_META_REQUIRES_CPC_SUPPORT);
36 
37 // Several of these tests have two variants to support running on development and release kernels.
38 // 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.
39 
40 // Metadata for running on a development kernel in CPC secure mode.
41 #define _T_META_CPC_SECURE_ON_DEV T_META_SYSCTL_INT("kern.cpc.secure=1"), XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL
42 
43 static void
_assert_kpep_ok(int kpep_err,const char * fmt,...)44 _assert_kpep_ok(int kpep_err, const char *fmt, ...)
45 {
46 	char msg[1024] = "";
47 	va_list args;
48 	va_start(args, fmt);
49 	vsnprintf(msg, sizeof(msg), fmt, args);
50 	va_end(args);
51 	T_QUIET;
52 	T_ASSERT_EQ(kpep_err, KPEP_ERR_NONE, "%s: %s", msg, kpep_strerror(kpep_err));
53 }
54 
55 static void
_skip_for_db(const char * kind,int kpep_err)56 _skip_for_db(const char *kind, int kpep_err)
57 {
58 	const char * const public_kpep_path = "/usr/share/kpep";
59 	const char * const internal_kpep_path = "/usr/local/share/kpep";
60 	const char * const paths[2] = { public_kpep_path, internal_kpep_path, };
61 	for (int i = 0; i < 2; i++) {
62 		const char * const path = paths[i];
63 		T_LOG("contents of %s:", path);
64 		DIR *dir = opendir(path);
65 		if (dir) {
66 			struct dirent *entry = NULL;
67 			while ((entry = readdir(dir)) != NULL) {
68 				T_LOG("    %s", entry->d_name);
69 			}
70 			(void)closedir(dir);
71 		} else {
72 			T_LOG("failed to open directory: %s", strerror(errno));
73 		}
74 	}
75 	int cpu_family = 0;
76 	size_t family_size = sizeof(cpu_family);
77 	int ret = sysctlbyname("hw.cpufamily", &cpu_family, &family_size, NULL, 0);
78 	if (ret != 0) {
79 		T_LOG("HW CPU family: 0x%8x", cpu_family);
80 	} else {
81 		T_LOG("failed to get hw.cpufamily: %s", strerror(errno));
82 	}
83 	T_SKIP("cannot open %s event database: %s", kind, kpep_strerror(kpep_err));
84 }
85 
86 // Check that a secure kernel disallows restricted events.
87 
88 static void
check_secure_cpmu(void)89 check_secure_cpmu(void)
90 {
91 	kpep_db_t public_db = NULL;
92 	int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &public_db);
93 	if (ret != KPEP_ERR_NONE) {
94 		_skip_for_db("public", ret);
95 	}
96 	kpep_db_t internal_db = NULL;
97 	ret = kpep_db_createx(NULL, KPEP_DB_FLAG_INTERNAL_ONLY, &internal_db);
98 	if (ret != KPEP_ERR_NONE) {
99 		_skip_for_db("internal", ret);
100 	}
101 	const char *na = NULL;
102 	kpep_db_name(public_db, &na);
103 
104 	size_t internal_event_count = 0;
105 	ret = kpep_db_events_count(internal_db, &internal_event_count);
106 	_assert_kpep_ok(ret, "getting internal event count");
107 
108 	kpep_event_t *internal_events = calloc(internal_event_count,
109 	    sizeof(internal_events[0]));
110 	T_QUIET; T_WITH_ERRNO;
111 	T_ASSERT_NOTNULL(internal_events, "allocate space for internal events");
112 
113 	ret = kpep_db_events(internal_db, internal_events,
114 	    internal_event_count * sizeof(internal_events[0]));
115 	_assert_kpep_ok(ret, "getting internal events");
116 
117 	kpep_config_t config = NULL;
118 	ret = kpep_config_create(internal_db, &config);
119 	_assert_kpep_ok(ret, "creating event configuration");
120 	ret = kpep_config_force_counters(config);
121 	_assert_kpep_ok(ret, "forcing counters with configuration");
122 
123 	size_t kpc_count_allocated = 0;
124 	uint64_t *kpc_configs = NULL;
125 	size_t *kpc_map = NULL;
126 
127 	unsigned int tested = 0;
128 	unsigned int filtered = 0;
129 	unsigned int public_tested = 0;
130 	for (size_t i = 0; i < internal_event_count; i++) {
131 		kpep_event_t event = internal_events[i];
132 		const char *name = NULL;
133 		ret = kpep_event_alias(event, &name);
134 		if (!name) {
135 			ret = kpep_event_name(event, &name);
136 		}
137 		_assert_kpep_ok(ret, "getting event name");
138 		if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
139 			T_LOG("skipping non-configurable %s event", name);
140 			continue;
141 		}
142 		bool empty_event = strcmp(name, "NO_EVNT") == 0;
143 		if (empty_event) {
144 			continue;
145 		}
146 
147 		kpep_event_t public_event = NULL;
148 		ret = kpep_db_event(public_db, name, &public_event);
149 		bool internal_only = ret == KPEP_ERR_EVENT_NOT_FOUND;
150 		ret = kpep_config_add_event(config, &event, 0, NULL);
151 		_assert_kpep_ok(ret, "adding event %s to configuration", name);
152 
153 		size_t kpc_count = 0;
154 		ret = kpep_config_kpc_count(config, &kpc_count);
155 		_assert_kpep_ok(ret, "getting KPC count");
156 		if (kpc_count == 0) {
157 			T_LOG("skipping event %s with no configuration", name);
158 			ret = kpep_config_remove_event(config, 0);
159 			_assert_kpep_ok(ret, "removing event %s from configuration", name);
160 			continue;
161 		}
162 		if (kpc_count > kpc_count_allocated) {
163 			free(kpc_configs);
164 			kpc_configs = calloc(kpc_count, sizeof(kpc_config_t));
165 			T_QUIET; T_WITH_ERRNO;
166 			T_ASSERT_NOTNULL(kpc_configs, "allocate space for KPC configs");
167 
168 			free(kpc_map);
169 			kpc_map = calloc(kpc_count, sizeof(size_t));
170 			T_QUIET; T_WITH_ERRNO;
171 			T_ASSERT_NOTNULL(kpc_map, "allocate space for KPC map");
172 
173 			kpc_count_allocated = kpc_count;
174 		}
175 
176 		ret = kpep_config_kpc_map(config, kpc_map, kpc_count * sizeof(size_t));
177 		_assert_kpep_ok(ret, "getting KPC map for event %s", name);
178 		ret = kpep_config_kpc(config, (uint8_t *)kpc_configs, kpc_count * sizeof(kpc_config_t));
179 		_assert_kpep_ok(ret, "getting KPC configs for event %s", name);
180 		kpc_config_t kpc_config = kpc_configs[kpc_map[0]];
181 
182 		ret = kpep_config_apply(config);
183 		bool not_permitted = ret == KPEP_ERR_ERRNO && errno == EPERM;
184 		if (not_permitted) {
185 			if (!internal_only) {
186 				T_LOG("failed to configure public event %s (0x%04llx)", name, kpc_config);
187 			}
188 			filtered++;
189 		} else if (internal_only) {
190 			T_FAIL("configured internal-only event %s (0x%04llx) with secure CPC", name, kpc_config);
191 		} else {
192 			public_tested++;
193 		}
194 		ret = kpep_config_remove_event(config, 0);
195 		_assert_kpep_ok(ret, "removing event %s from configuration", name);
196 		tested++;
197 	}
198 
199 	T_LOG("tested %u internal/public events", tested);
200 	T_LOG("correctly permitted to configure %u public events", public_tested);
201 	T_LOG("correctly not permitted to configure %u internal-only events",
202 	    filtered);
203 	kpep_config_free(config);
204 	kpep_db_free(public_db);
205 	kpep_db_free(internal_db);
206 	free(kpc_configs);
207 	free(kpc_map);
208 }
209 
210 T_DECL(secure_cpmu_event_restrictions, "secured CPMU should be restricted to known events",
211     _T_META_CPC_SECURE_ON_DEV,
212     T_META_TAG_VM_NOT_ELIGIBLE,
213     T_META_ENABLED(false) /* rdar://153473281 */)
214 {
215 	check_secure_cpmu();
216 }
217 
218 T_DECL(release_cpmu_event_restrictions, "release CPMU should be restricted to known events",
219     XNU_T_META_REQUIRES_RELEASE_KERNEL,
220     T_META_TAG_VM_NOT_ELIGIBLE,
221     T_META_ENABLED(false) /* rdar://153473334 */)
222 {
223 	check_secure_cpmu();
224 }
225 
226 #define UNCORE_DEV_PATH "/dev/monotonic/uncore"
227 #define UPMU_REF_CYCLES 0x02
228 
229 static void
check_secure_upmu(void)230 check_secure_upmu(void)
231 {
232 	guardid_t guard;
233 	int fd;
234 
235 	guard = 0xa5adcafe;
236 
237 	T_SETUPBEGIN;
238 
239 	fd = guarded_open_np(UNCORE_DEV_PATH, &guard,
240 	    GUARD_CLOSE | GUARD_DUP | GUARD_WRITE, O_CLOEXEC | O_EXCL);
241 	if (fd < 0 && errno == ENOENT) {
242 		T_SKIP("uncore counters are unsupported");
243 	}
244 
245 	union monotonic_ctl_add add_ctl = {
246 		.in.config.event = UPMU_REF_CYCLES,
247 		.in.config.allowed_ctr_mask = 0xffff,
248 	};
249 
250 	T_SETUPEND;
251 
252 	int ret = ioctl(fd, MT_IOC_ADD, &add_ctl);
253 	T_EXPECT_POSIX_FAILURE(ret, EPERM,
254 	    "should not be allowed to count any events on UPMU");
255 }
256 
257 T_DECL(secure_upmu_event_restrictions, "secured UPMU should be restricted to no events",
258     _T_META_CPC_SECURE_ON_DEV, T_META_TAG_VM_NOT_ELIGIBLE)
259 {
260 	check_secure_upmu();
261 }
262 
263 T_DECL(release_upmu_event_restrictions, "release UPMU should be restricted to no events",
264     XNU_T_META_REQUIRES_RELEASE_KERNEL, T_META_TAG_VM_NOT_ELIGIBLE)
265 {
266 	check_secure_upmu();
267 }
268 
269 // Check that events which are exposed publicly are allowed to be configured.
270 
271 static void
check_event_coverage(kpep_db_flags_t flag,const char * kind)272 check_event_coverage(kpep_db_flags_t flag, const char *kind)
273 {
274 	kpep_db_t db = NULL;
275 	int ret = kpep_db_createx(NULL, flag, &db);
276 	_assert_kpep_ok(ret, "creating %s event database", kind);
277 
278 	size_t event_count = 0;
279 	ret = kpep_db_events_count(db, &event_count);
280 	_assert_kpep_ok(ret, "getting %s event count", kind);
281 
282 	kpep_event_t *events = calloc(event_count, sizeof(events[0]));
283 	T_QUIET; T_WITH_ERRNO;
284 	T_ASSERT_NOTNULL(events, "allocate space for events");
285 
286 	ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
287 	_assert_kpep_ok(ret, "getting public events");
288 
289 	kpep_config_t config = NULL;
290 	ret = kpep_config_create(db, &config);
291 	_assert_kpep_ok(ret, "creating event configuration");
292 	ret = kpep_config_force_counters(config);
293 	_assert_kpep_ok(ret, "forcing counters with configuration");
294 
295 	size_t kpc_count_allocated = 0;
296 	uint64_t *kpc_configs = NULL;
297 	size_t *kpc_map = NULL;
298 
299 	unsigned int tested = 0;
300 	for (size_t i = 0; i < event_count; i++) {
301 		kpep_event_t event = events[i];
302 		const char *name = NULL;
303 		ret = kpep_event_name(event, &name);
304 		_assert_kpep_ok(ret, "getting event name");
305 		if (strncmp(name, "FIXED", strlen("FIXED")) == 0) {
306 			T_LOG("skipping non-configurable %s event", name);
307 			continue;
308 		}
309 
310 		ret = kpep_config_add_event(config, &event, 0, NULL);
311 		_assert_kpep_ok(ret, "adding event %s to configuration", name);
312 
313 		size_t kpc_count = 0;
314 		ret = kpep_config_kpc_count(config, &kpc_count);
315 		_assert_kpep_ok(ret, "getting KPC count");
316 		if (kpc_count > kpc_count_allocated) {
317 			free(kpc_configs);
318 			kpc_configs = calloc(kpc_count, sizeof(kpc_config_t));
319 			T_QUIET; T_WITH_ERRNO;
320 			T_ASSERT_NOTNULL(kpc_configs, "allocate space for KPC configs");
321 			kpc_count_allocated = kpc_count;
322 
323 			free(kpc_map);
324 			kpc_map = calloc(kpc_count, sizeof(size_t));
325 			T_QUIET; T_WITH_ERRNO;
326 			T_ASSERT_NOTNULL(kpc_map, "allocate space for KPC map");
327 		}
328 
329 		ret = kpep_config_kpc_map(config, kpc_map, kpc_count * sizeof(size_t));
330 		_assert_kpep_ok(ret, "getting KPC map for event %s", name);
331 		ret = kpep_config_kpc(config, (uint8_t *)kpc_configs, kpc_count * sizeof(kpc_config_t));
332 		_assert_kpep_ok(ret, "getting KPC configs for event %s", name);
333 		kpc_config_t kpc_config = kpc_configs[kpc_map[0]];
334 
335 		ret = kpep_config_apply(config);
336 		if (ret == KPEP_ERR_ERRNO && errno == EPERM) {
337 			T_FAIL("failed to configure %s event %s (0x%04llx) with secure CPC", kind, name, kpc_config);
338 		} else {
339 			_assert_kpep_ok(ret, "applying configuration with event %s (0x%04llx)", name, kpc_config);
340 		}
341 		ret = kpep_config_remove_event(config, 0);
342 		_assert_kpep_ok(ret, "removing event %s from configuration", name);
343 		tested++;
344 	}
345 
346 	T_LOG("attempted to configure %u %s events", tested, kind);
347 	kpep_config_free(config);
348 	kpep_db_free(db);
349 	free(kpc_configs);
350 	free(kpc_map);
351 }
352 
353 T_DECL(secure_public_event_coverage, "all public events in kpep should be allowed",
354     _T_META_CPC_SECURE_ON_DEV, T_META_TAG_VM_NOT_ELIGIBLE)
355 {
356 	check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
357 }
358 
359 T_DECL(release_public_event_coverage, "all public events in kpep should be allowed",
360     XNU_T_META_REQUIRES_RELEASE_KERNEL, T_META_TAG_VM_NOT_ELIGIBLE)
361 {
362 	check_event_coverage(KPEP_DB_FLAG_PUBLIC_ONLY, "public");
363 }
364 
365 // Check for internal development behaviors.
366 T_DECL(insecure_cpmu_unrestricted, "insecure CPMU should be unrestricted",
367     XNU_T_META_REQUIRES_DEVELOPMENT_KERNEL, T_META_SYSCTL_INT("kern.cpc.secure=0"), T_META_TAG_VM_NOT_ELIGIBLE)
368 {
369 	check_event_coverage(KPEP_DB_FLAG_INTERNAL_ONLY, "internal");
370 }
371 
372 T_DECL(secure_kpc_counting_system, "kpc should not allow counting the kernel when secure",
373     _T_META_CPC_SECURE_ON_DEV,
374     T_META_ENABLED(false) /* rdar://131466526 */)
375 {
376 	kpep_db_t db = NULL;
377 	int ret = kpep_db_createx(NULL, KPEP_DB_FLAG_PUBLIC_ONLY, &db);
378 	_assert_kpep_ok(ret, "creating public event database");
379 
380 	size_t event_count = 0;
381 	ret = kpep_db_events_count(db, &event_count);
382 	_assert_kpep_ok(ret, "getting public event count");
383 
384 	kpep_event_t *events = calloc(event_count, sizeof(events[0]));
385 	T_QUIET; T_WITH_ERRNO;
386 	T_ASSERT_NOTNULL(events, "allocate space for events");
387 
388 	ret = kpep_db_events(db, events, event_count * sizeof(events[0]));
389 	_assert_kpep_ok(ret, "getting public events");
390 
391 	kpep_config_t config = NULL;
392 	ret = kpep_config_create(db, &config);
393 	_assert_kpep_ok(ret, "creating event configuration");
394 	ret = kpep_config_force_counters(config);
395 	_assert_kpep_ok(ret, "forcing counters with configuration");
396 
397 
398 	kpep_event_t event = NULL;
399 	const char *name = NULL;
400 	for (size_t i = 0; i < event_count; i++) {
401 		event = events[i];
402 		ret = kpep_event_name(event, &name);
403 		_assert_kpep_ok(ret, "getting event name");
404 		if (strncmp(name, "FIXED", strlen("FIXED")) != 0) {
405 			break;
406 		}
407 		T_LOG("skipping non-configurable %s event", name);
408 	}
409 
410 	ret = kpep_config_add_event(config, &event, KPEP_EVENT_FLAG_KERNEL, NULL);
411 	_assert_kpep_ok(ret, "adding event %s to configuration", name);
412 
413 	ret = kpep_config_apply(config);
414 	_assert_kpep_ok(ret, "applying configuration with event %s", name);
415 
416 	uint32_t config_count = kpc_get_config_count(KPC_CLASS_CONFIGURABLE_MASK);
417 	uint64_t *configs = calloc(config_count, sizeof(configs[0]));
418 	T_QUIET;
419 	T_ASSERT_NOTNULL(configs, "allocated %u * %zu", config_count, sizeof(configs[0]));
420 	ret = kpc_get_config(KPC_CLASS_CONFIGURABLE_MASK, configs);
421 	for (uint32_t i = 0; i < config_count; i++) {
422 		if ((configs[i] & 0x40000)) {
423 			T_FAIL("found configurable counter %u with configuration 0x%llx", i, configs[i]);
424 		}
425 	}
426 	T_LOG("checked %d events for EL2 counting", config_count);
427 
428 	kpep_config_free(config);
429 	kpep_db_free(db);
430 }
431