xref: /linux/tools/testing/selftests/landlock/ptrace_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Landlock tests - Ptrace
4  *
5  * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6  * Copyright © 2019-2020 ANSSI
7  * Copyright © 2024-2025 Microsoft Corporation
8  */
9 
10 #define _GNU_SOURCE
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <linux/landlock.h>
14 #include <sched.h>
15 #include <signal.h>
16 #include <sys/mount.h>
17 #include <sys/prctl.h>
18 #include <sys/ptrace.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22 
23 #include "audit.h"
24 #include "common.h"
25 #include "trace.h"
26 
27 /* Copied from security/yama/yama_lsm.c */
28 #define YAMA_SCOPE_DISABLED 0
29 #define YAMA_SCOPE_RELATIONAL 1
30 
31 static void create_domain(struct __test_metadata *const _metadata)
32 {
33 	int ruleset_fd;
34 	struct landlock_ruleset_attr ruleset_attr = {
35 		.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
36 	};
37 
38 	ruleset_fd =
39 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
40 	EXPECT_LE(0, ruleset_fd)
41 	{
42 		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
43 	}
44 	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
45 	EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
46 	EXPECT_EQ(0, close(ruleset_fd));
47 }
48 
49 static int test_ptrace_read(const pid_t pid)
50 {
51 	static const char path_template[] = "/proc/%d/environ";
52 	char procenv_path[sizeof(path_template) + 10];
53 	int procenv_path_size, fd;
54 
55 	procenv_path_size = snprintf(procenv_path, sizeof(procenv_path),
56 				     path_template, pid);
57 	if (procenv_path_size >= sizeof(procenv_path))
58 		return E2BIG;
59 
60 	fd = open(procenv_path, O_RDONLY | O_CLOEXEC);
61 	if (fd < 0)
62 		return errno;
63 	/*
64 	 * Mixing error codes from close(2) and open(2) should not lead to any
65 	 * (access type) confusion for this test.
66 	 */
67 	if (close(fd) != 0)
68 		return errno;
69 	return 0;
70 }
71 
72 static int get_yama_ptrace_scope(void)
73 {
74 	int ret;
75 	char buf[2] = {};
76 	const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
77 
78 	if (fd < 0)
79 		return 0;
80 
81 	if (read(fd, buf, 1) < 0) {
82 		close(fd);
83 		return -1;
84 	}
85 
86 	ret = atoi(buf);
87 	close(fd);
88 	return ret;
89 }
90 
91 /* clang-format off */
92 FIXTURE(scoped_domains) {};
93 /* clang-format on */
94 
95 /*
96  * Test multiple tracing combinations between a parent process P1 and a child
97  * process P2.
98  *
99  * Yama's scoped ptrace is presumed disabled.  If enabled, this optional
100  * restriction is enforced in addition to any Landlock check, which means that
101  * all P2 requests to trace P1 would be denied.
102  */
103 #include "scoped_base_variants.h"
104 
105 FIXTURE_SETUP(scoped_domains)
106 {
107 }
108 
109 FIXTURE_TEARDOWN(scoped_domains)
110 {
111 }
112 
113 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
114 TEST_F(scoped_domains, trace)
115 {
116 	pid_t child, parent;
117 	int status, err_proc_read;
118 	int pipe_child[2], pipe_parent[2];
119 	int yama_ptrace_scope;
120 	char buf_parent;
121 	long ret;
122 	bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
123 
124 	yama_ptrace_scope = get_yama_ptrace_scope();
125 	ASSERT_LE(0, yama_ptrace_scope);
126 
127 	if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
128 		TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
129 		       yama_ptrace_scope);
130 
131 	/*
132 	 * can_read_child is true if a parent process can read its child
133 	 * process, which is only the case when the parent process is not
134 	 * isolated from the child with a dedicated Landlock domain.
135 	 */
136 	can_read_child = !variant->domain_parent;
137 
138 	/*
139 	 * can_trace_child is true if a parent process can trace its child
140 	 * process.  This depends on two conditions:
141 	 * - The parent process is not isolated from the child with a dedicated
142 	 *   Landlock domain.
143 	 * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
144 	 */
145 	can_trace_child = can_read_child &&
146 			  yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
147 
148 	/*
149 	 * can_read_parent is true if a child process can read its parent
150 	 * process, which is only the case when the child process is not
151 	 * isolated from the parent with a dedicated Landlock domain.
152 	 */
153 	can_read_parent = !variant->domain_child;
154 
155 	/*
156 	 * can_trace_parent is true if a child process can trace its parent
157 	 * process.  This depends on two conditions:
158 	 * - The child process is not isolated from the parent with a dedicated
159 	 *   Landlock domain.
160 	 * - Yama is disabled (YAMA_SCOPE_DISABLED).
161 	 */
162 	can_trace_parent = can_read_parent &&
163 			   yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
164 
165 	/*
166 	 * Removes all effective and permitted capabilities to not interfere
167 	 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
168 	 */
169 	drop_caps(_metadata);
170 
171 	parent = getpid();
172 	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
173 	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
174 	if (variant->domain_both) {
175 		create_domain(_metadata);
176 		if (!__test_passed(_metadata))
177 			/* Aborts before forking. */
178 			return;
179 	}
180 
181 	child = fork();
182 	ASSERT_LE(0, child);
183 	if (child == 0) {
184 		char buf_child;
185 
186 		ASSERT_EQ(0, close(pipe_parent[1]));
187 		ASSERT_EQ(0, close(pipe_child[0]));
188 		if (variant->domain_child)
189 			create_domain(_metadata);
190 
191 		/* Waits for the parent to be in a domain, if any. */
192 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
193 
194 		/* Tests PTRACE_MODE_READ on the parent. */
195 		err_proc_read = test_ptrace_read(parent);
196 		if (can_read_parent) {
197 			EXPECT_EQ(0, err_proc_read);
198 		} else {
199 			EXPECT_EQ(EACCES, err_proc_read);
200 		}
201 
202 		/* Tests PTRACE_ATTACH on the parent. */
203 		ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
204 		if (can_trace_parent) {
205 			EXPECT_EQ(0, ret);
206 		} else {
207 			EXPECT_EQ(-1, ret);
208 			EXPECT_EQ(EPERM, errno);
209 		}
210 		if (ret == 0) {
211 			ASSERT_EQ(parent, waitpid(parent, &status, 0));
212 			ASSERT_EQ(1, WIFSTOPPED(status));
213 			ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0));
214 		}
215 
216 		/* Tests child PTRACE_TRACEME. */
217 		ret = ptrace(PTRACE_TRACEME);
218 		if (can_trace_child) {
219 			EXPECT_EQ(0, ret);
220 		} else {
221 			EXPECT_EQ(-1, ret);
222 			EXPECT_EQ(EPERM, errno);
223 		}
224 
225 		/*
226 		 * Signals that the PTRACE_ATTACH test is done and the
227 		 * PTRACE_TRACEME test is ongoing.
228 		 */
229 		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
230 
231 		if (can_trace_child) {
232 			ASSERT_EQ(0, raise(SIGSTOP));
233 		}
234 
235 		/* Waits for the parent PTRACE_ATTACH test. */
236 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
237 		_exit(_metadata->exit_code);
238 		return;
239 	}
240 
241 	ASSERT_EQ(0, close(pipe_child[1]));
242 	ASSERT_EQ(0, close(pipe_parent[0]));
243 	if (variant->domain_parent)
244 		create_domain(_metadata);
245 
246 	/* Signals that the parent is in a domain, if any. */
247 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
248 
249 	/*
250 	 * Waits for the child to test PTRACE_ATTACH on the parent and start
251 	 * testing PTRACE_TRACEME.
252 	 */
253 	ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
254 
255 	/* Tests child PTRACE_TRACEME. */
256 	if (can_trace_child) {
257 		ASSERT_EQ(child, waitpid(child, &status, 0));
258 		ASSERT_EQ(1, WIFSTOPPED(status));
259 		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
260 	} else {
261 		/* The child should not be traced by the parent. */
262 		EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
263 		EXPECT_EQ(ESRCH, errno);
264 	}
265 
266 	/* Tests PTRACE_MODE_READ on the child. */
267 	err_proc_read = test_ptrace_read(child);
268 	if (can_read_child) {
269 		EXPECT_EQ(0, err_proc_read);
270 	} else {
271 		EXPECT_EQ(EACCES, err_proc_read);
272 	}
273 
274 	/* Tests PTRACE_ATTACH on the child. */
275 	ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
276 	if (can_trace_child) {
277 		EXPECT_EQ(0, ret);
278 	} else {
279 		EXPECT_EQ(-1, ret);
280 		EXPECT_EQ(EPERM, errno);
281 	}
282 
283 	if (ret == 0) {
284 		ASSERT_EQ(child, waitpid(child, &status, 0));
285 		ASSERT_EQ(1, WIFSTOPPED(status));
286 		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
287 	}
288 
289 	/* Signals that the parent PTRACE_ATTACH test is done. */
290 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
291 	ASSERT_EQ(child, waitpid(child, &status, 0));
292 
293 	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
294 	    WEXITSTATUS(status) != EXIT_SUCCESS)
295 		_metadata->exit_code = KSFT_FAIL;
296 }
297 
298 static int matches_log_ptrace(struct __test_metadata *const _metadata,
299 			      int audit_fd, const pid_t opid)
300 {
301 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
302 		" blockers=ptrace opid=%d ocomm=\"ptrace_test\"$";
303 	char log_match[sizeof(log_template) + 10];
304 	int log_match_len;
305 
306 	log_match_len =
307 		snprintf(log_match, sizeof(log_match), log_template, opid);
308 	if (log_match_len > sizeof(log_match))
309 		return -E2BIG;
310 
311 	return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
312 				  NULL);
313 }
314 
315 FIXTURE(audit)
316 {
317 	struct audit_filter audit_filter;
318 	int audit_fd;
319 };
320 
321 FIXTURE_SETUP(audit)
322 {
323 	disable_caps(_metadata);
324 	set_cap(_metadata, CAP_AUDIT_CONTROL);
325 	self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
326 	EXPECT_LE(0, self->audit_fd);
327 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
328 }
329 
330 FIXTURE_TEARDOWN_PARENT(audit)
331 {
332 	EXPECT_EQ(0, audit_cleanup(-1, NULL));
333 }
334 
335 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
336 TEST_F(audit, trace)
337 {
338 	pid_t child;
339 	int status;
340 	int pipe_child[2], pipe_parent[2];
341 	int yama_ptrace_scope;
342 	char buf_parent;
343 	struct audit_records records;
344 
345 	/* Makes sure there is no superfluous logged records. */
346 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
347 	EXPECT_EQ(0, records.access);
348 	EXPECT_EQ(0, records.domain);
349 
350 	yama_ptrace_scope = get_yama_ptrace_scope();
351 	ASSERT_LE(0, yama_ptrace_scope);
352 
353 	if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
354 		TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
355 		       yama_ptrace_scope);
356 
357 	/*
358 	 * Removes all effective and permitted capabilities to not interfere
359 	 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
360 	 */
361 	drop_caps(_metadata);
362 
363 	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
364 	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
365 
366 	child = fork();
367 	ASSERT_LE(0, child);
368 	if (child == 0) {
369 		char buf_child;
370 
371 		ASSERT_EQ(0, close(pipe_parent[1]));
372 		ASSERT_EQ(0, close(pipe_child[0]));
373 
374 		/* Waits for the parent to be in a domain, if any. */
375 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
376 
377 		/* Tests child PTRACE_TRACEME. */
378 		EXPECT_EQ(-1, ptrace(PTRACE_TRACEME));
379 		EXPECT_EQ(EPERM, errno);
380 		/* We should see the child process. */
381 		EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd,
382 						getpid()));
383 
384 		EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
385 		EXPECT_EQ(0, records.access);
386 		/* Checks for a domain creation. */
387 		EXPECT_EQ(1, records.domain);
388 
389 		/*
390 		 * Signals that the PTRACE_ATTACH test is done and the
391 		 * PTRACE_TRACEME test is ongoing.
392 		 */
393 		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
394 
395 		/* Waits for the parent PTRACE_ATTACH test. */
396 		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
397 		_exit(_metadata->exit_code);
398 		return;
399 	}
400 
401 	ASSERT_EQ(0, close(pipe_child[1]));
402 	ASSERT_EQ(0, close(pipe_parent[0]));
403 	create_domain(_metadata);
404 
405 	/* Signals that the parent is in a domain. */
406 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
407 
408 	/*
409 	 * Waits for the child to test PTRACE_ATTACH on the parent and start
410 	 * testing PTRACE_TRACEME.
411 	 */
412 	ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
413 
414 	/* The child should not be traced by the parent. */
415 	EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
416 	EXPECT_EQ(ESRCH, errno);
417 
418 	/* Tests PTRACE_ATTACH on the child. */
419 	EXPECT_EQ(-1, ptrace(PTRACE_ATTACH, child, NULL, 0));
420 	EXPECT_EQ(EPERM, errno);
421 	EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd, child));
422 
423 	/* Signals that the parent PTRACE_ATTACH test is done. */
424 	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
425 	ASSERT_EQ(child, waitpid(child, &status, 0));
426 	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
427 	    WEXITSTATUS(status) != EXIT_SUCCESS)
428 		_metadata->exit_code = KSFT_FAIL;
429 
430 	/* Makes sure there is no superfluous logged records. */
431 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
432 	EXPECT_EQ(0, records.access);
433 	EXPECT_EQ(0, records.domain);
434 }
435 
436 /* Trace tests */
437 
438 /* clang-format off */
439 FIXTURE(trace_ptrace) {
440 	/* clang-format on */
441 	int tracefs_ok;
442 };
443 
444 FIXTURE_SETUP(trace_ptrace)
445 {
446 	int ret;
447 
448 	set_cap(_metadata, CAP_SYS_ADMIN);
449 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
450 	ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
451 
452 	ret = tracefs_fixture_setup();
453 	if (ret) {
454 		clear_cap(_metadata, CAP_SYS_ADMIN);
455 		self->tracefs_ok = 0;
456 		SKIP(return, "tracefs not available");
457 	}
458 	self->tracefs_ok = 1;
459 
460 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, true));
461 	ASSERT_EQ(0, tracefs_clear());
462 	clear_cap(_metadata, CAP_SYS_ADMIN);
463 }
464 
465 FIXTURE_TEARDOWN(trace_ptrace)
466 {
467 	if (!self->tracefs_ok)
468 		return;
469 
470 	set_cap(_metadata, CAP_SYS_ADMIN);
471 	tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, false);
472 	tracefs_fixture_teardown();
473 	clear_cap(_metadata, CAP_SYS_ADMIN);
474 }
475 
476 /* clang-format off */
477 FIXTURE_VARIANT(trace_ptrace)
478 {
479 	/* clang-format on */
480 	bool sandbox;
481 	bool sandbox_target;
482 	int expect_denied;
483 };
484 
485 /* Denied: sandboxed child ptraces unsandboxed parent (tracee_domain=0). */
486 /* clang-format off */
487 FIXTURE_VARIANT_ADD(trace_ptrace, denied) {
488 	/* clang-format on */
489 	.sandbox = true,
490 	.sandbox_target = false,
491 	.expect_denied = 1,
492 };
493 
494 /*
495  * Denied: sandboxed child ptraces a sandboxed parent, so the tracee is in a
496  * domain and tracee_domain= is non-zero.
497  */
498 /* clang-format off */
499 FIXTURE_VARIANT_ADD(trace_ptrace, denied_scoped_target) {
500 	/* clang-format on */
501 	.sandbox = true,
502 	.sandbox_target = true,
503 	.expect_denied = 1,
504 };
505 
506 /* Allowed: unsandboxed child uses PTRACE_TRACEME. */
507 /* clang-format off */
508 FIXTURE_VARIANT_ADD(trace_ptrace, allowed) {
509 	/* clang-format on */
510 	.sandbox = false,
511 	.sandbox_target = false,
512 	.expect_denied = 0,
513 };
514 
515 TEST_F(trace_ptrace, deny_ptrace)
516 {
517 	char *buf, field[64], expected_pid[16];
518 	int count, status;
519 	pid_t child, parent;
520 
521 	if (!self->tracefs_ok)
522 		SKIP(return, "tracefs not available");
523 
524 	parent = getpid();
525 
526 	/*
527 	 * Set a known comm so the denied variant can verify both the trace line
528 	 * task name and the tracee_comm= field.
529 	 */
530 	prctl(PR_SET_NAME, "ll_trace_test");
531 
532 	/*
533 	 * For the non-zero tracee_domain case, sandbox the parent (the tracee)
534 	 * before forking.  The child inherits that domain and adds its own
535 	 * layer, so the child (tracer) is not an ancestor of the tracee and the
536 	 * ptrace is still denied, with tracee_domain= naming the parent's
537 	 * domain.
538 	 */
539 	if (variant->sandbox_target)
540 		create_domain(_metadata);
541 
542 	child = fork();
543 	ASSERT_LE(0, child);
544 
545 	if (child == 0) {
546 		if (variant->sandbox) {
547 			struct landlock_ruleset_attr ruleset_attr = {
548 				.scoped = LANDLOCK_SCOPE_SIGNAL,
549 			};
550 			int ruleset_fd;
551 
552 			/*
553 			 * Any scope creates a domain.  Ptrace denial checks
554 			 * domain ancestry, not specific flags.
555 			 */
556 			ruleset_fd = landlock_create_ruleset(
557 				&ruleset_attr, sizeof(ruleset_attr), 0);
558 			if (ruleset_fd < 0)
559 				_exit(1);
560 
561 			prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
562 			if (landlock_restrict_self(ruleset_fd, 0)) {
563 				close(ruleset_fd);
564 				_exit(1);
565 			}
566 			close(ruleset_fd);
567 
568 			/* PTRACE_ATTACH on unsandboxed parent: denied. */
569 			if (ptrace(PTRACE_ATTACH, parent, NULL, NULL) == 0) {
570 				ptrace(PTRACE_DETACH, parent, NULL, NULL);
571 				_exit(2);
572 			}
573 			if (errno != EPERM)
574 				_exit(3);
575 		} else {
576 			/* No sandbox: ptrace should succeed. */
577 			if (ptrace(PTRACE_TRACEME) != 0)
578 				_exit(1);
579 		}
580 
581 		_exit(0);
582 	}
583 
584 	ASSERT_EQ(child, waitpid(child, &status, 0));
585 	ASSERT_TRUE(WIFEXITED(status));
586 	EXPECT_EQ(0, WEXITSTATUS(status));
587 
588 	buf = tracefs_read_buf();
589 	ASSERT_NE(NULL, buf);
590 
591 	count = tracefs_count_matches(buf, REGEX_DENY_PTRACE("ll_trace_test"));
592 	if (variant->expect_denied) {
593 		EXPECT_EQ(variant->expect_denied, count)
594 		{
595 			TH_LOG("Expected deny_ptrace event, got %d\n%s", count,
596 			       buf);
597 		}
598 
599 		/* Verify tracee_pid is the parent's TGID. */
600 		snprintf(expected_pid, sizeof(expected_pid), "%d", parent);
601 		ASSERT_EQ(0, tracefs_extract_field(
602 				     buf, REGEX_DENY_PTRACE("ll_trace_test"),
603 				     "tracee_pid", field, sizeof(field)));
604 		EXPECT_STREQ(expected_pid, field);
605 
606 		/* Verify tracee_comm matches prctl(PR_SET_NAME). */
607 		ASSERT_EQ(0, tracefs_extract_field(
608 				     buf, REGEX_DENY_PTRACE("ll_trace_test"),
609 				     "tracee_comm", field, sizeof(field)));
610 		EXPECT_STREQ("ll_trace_test", field);
611 
612 		/*
613 		 * Verify tracee_domain: 0 when the tracee is unsandboxed,
614 		 * non-zero when the tracee is in a domain.
615 		 */
616 		ASSERT_EQ(0, tracefs_extract_field(
617 				     buf, REGEX_DENY_PTRACE("ll_trace_test"),
618 				     "tracee_domain", field, sizeof(field)));
619 		EXPECT_EQ(variant->sandbox_target, strcmp("0", field) != 0)
620 		{
621 			TH_LOG("Unexpected tracee_domain=%s", field);
622 		}
623 	} else {
624 		EXPECT_EQ(0, count)
625 		{
626 			TH_LOG("Expected 0 deny_ptrace events, got %d\n%s",
627 			       count, buf);
628 		}
629 	}
630 
631 	free(buf);
632 }
633 
634 /* clang-format off */
635 FIXTURE(trace_ptrace_traceme) {
636 	/* clang-format on */
637 	int tracefs_ok;
638 };
639 
640 FIXTURE_SETUP(trace_ptrace_traceme)
641 {
642 	int ret;
643 
644 	set_cap(_metadata, CAP_SYS_ADMIN);
645 	ASSERT_EQ(0, unshare(CLONE_NEWNS));
646 	ASSERT_EQ(0, mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL));
647 
648 	ret = tracefs_fixture_setup();
649 	if (ret) {
650 		clear_cap(_metadata, CAP_SYS_ADMIN);
651 		self->tracefs_ok = 0;
652 		SKIP(return, "tracefs not available");
653 	}
654 	self->tracefs_ok = 1;
655 
656 	ASSERT_EQ(0, tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, true));
657 	ASSERT_EQ(0, tracefs_clear());
658 	clear_cap(_metadata, CAP_SYS_ADMIN);
659 }
660 
661 FIXTURE_TEARDOWN(trace_ptrace_traceme)
662 {
663 	if (!self->tracefs_ok)
664 		return;
665 
666 	set_cap(_metadata, CAP_SYS_ADMIN);
667 	tracefs_enable_event(TRACEFS_DENY_PTRACE_ENABLE, false);
668 	tracefs_fixture_teardown();
669 	clear_cap(_metadata, CAP_SYS_ADMIN);
670 }
671 
672 /* clang-format off */
673 FIXTURE_VARIANT(trace_ptrace_traceme)
674 {
675 	/* clang-format on */
676 	bool sandbox_tracer;
677 	bool sandbox_tracee;
678 	int expect_denied;
679 };
680 
681 /*
682  * Denied: a sandboxed tracer cannot trace the unsandboxed child that asked to
683  * be traced with PTRACE_TRACEME (tracee_domain=0).
684  */
685 /* clang-format off */
686 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, denied) {
687 	/* clang-format on */
688 	.sandbox_tracer = true,
689 	.sandbox_tracee = false,
690 	.expect_denied = 1,
691 };
692 
693 /*
694  * Denied: a sandboxed child in its own domain asks to be traced by a tracer in
695  * an unrelated domain, so the tracee is in a domain and tracee_domain= is
696  * non-zero.
697  */
698 /* clang-format off */
699 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, denied_scoped_tracee) {
700 	/* clang-format on */
701 	.sandbox_tracer = true,
702 	.sandbox_tracee = true,
703 	.expect_denied = 1,
704 };
705 
706 /* Allowed: unsandboxed child uses PTRACE_TRACEME with an unsandboxed tracer. */
707 /* clang-format off */
708 FIXTURE_VARIANT_ADD(trace_ptrace_traceme, allowed) {
709 	/* clang-format on */
710 	.sandbox_tracer = false,
711 	.sandbox_tracee = false,
712 	.expect_denied = 0,
713 };
714 
715 TEST_F(trace_ptrace_traceme, deny_ptrace)
716 {
717 	char *buf, field[64], expected_pid[16];
718 	int count, status, sync_pipe[2];
719 	pid_t child;
720 
721 	if (!self->tracefs_ok)
722 		SKIP(return, "tracefs not available");
723 
724 	/*
725 	 * Set a known comm so the denied variant can verify both the trace line
726 	 * task name and the tracee_comm= field.  The tracee is the current
727 	 * (child) task for PTRACE_TRACEME, so the child inherits this name.
728 	 */
729 	prctl(PR_SET_NAME, "ll_trace_test");
730 
731 	ASSERT_EQ(0, pipe2(sync_pipe, O_CLOEXEC));
732 
733 	child = fork();
734 	ASSERT_LE(0, child);
735 
736 	if (child == 0) {
737 		char c;
738 
739 		close(sync_pipe[1]);
740 
741 		/*
742 		 * The tracee is the current task; for the non-zero
743 		 * tracee_domain case it sandboxes itself in its own domain,
744 		 * unrelated to the tracer's domain, so PTRACE_TRACEME is still
745 		 * denied and tracee_domain= names the child's own domain.
746 		 */
747 		if (variant->sandbox_tracee)
748 			create_domain(_metadata);
749 
750 		/* Waits for the tracer (parent) to enter its domain, if any. */
751 		if (read(sync_pipe[0], &c, 1) != 1)
752 			_exit(1);
753 		close(sync_pipe[0]);
754 
755 		if (variant->expect_denied) {
756 			if (ptrace(PTRACE_TRACEME) == 0)
757 				_exit(2);
758 			if (errno != EPERM)
759 				_exit(3);
760 		} else {
761 			if (ptrace(PTRACE_TRACEME) != 0)
762 				_exit(4);
763 			/* Lets the tracer reap the trace-stop and detach. */
764 			raise(SIGSTOP);
765 		}
766 
767 		_exit(0);
768 	}
769 
770 	close(sync_pipe[0]);
771 
772 	/*
773 	 * For a denial, the proposed tracer must be in a domain that is not an
774 	 * ancestor of the tracee's domain.  Sandboxing the parent after the
775 	 * fork gives it a domain unrelated to the child.
776 	 */
777 	if (variant->sandbox_tracer)
778 		create_domain(_metadata);
779 
780 	/* Signals the child that the tracer is in its domain, if any. */
781 	ASSERT_EQ(1, write(sync_pipe[1], ".", 1));
782 	close(sync_pipe[1]);
783 
784 	if (!variant->expect_denied) {
785 		/* PTRACE_TRACEME succeeded: reap the SIGSTOP and detach. */
786 		ASSERT_EQ(child, waitpid(child, &status, WUNTRACED));
787 		ASSERT_TRUE(WIFSTOPPED(status));
788 		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
789 	}
790 
791 	ASSERT_EQ(child, waitpid(child, &status, 0));
792 	ASSERT_TRUE(WIFEXITED(status));
793 	EXPECT_EQ(0, WEXITSTATUS(status));
794 
795 	buf = tracefs_read_buf();
796 	ASSERT_NE(NULL, buf);
797 
798 	count = tracefs_count_matches(buf, REGEX_DENY_PTRACE("ll_trace_test"));
799 	if (variant->expect_denied) {
800 		EXPECT_EQ(variant->expect_denied, count)
801 		{
802 			TH_LOG("Expected deny_ptrace event, got %d\n%s", count,
803 			       buf);
804 		}
805 
806 		/* Verify tracee_pid is the child's TGID (the traced task). */
807 		snprintf(expected_pid, sizeof(expected_pid), "%d", child);
808 		ASSERT_EQ(0, tracefs_extract_field(
809 				     buf, REGEX_DENY_PTRACE("ll_trace_test"),
810 				     "tracee_pid", field, sizeof(field)));
811 		EXPECT_STREQ(expected_pid, field);
812 
813 		/*
814 		 * Verify tracee_domain: 0 when the tracee is unsandboxed,
815 		 * non-zero when the tracee is in a domain.
816 		 */
817 		ASSERT_EQ(0, tracefs_extract_field(
818 				     buf, REGEX_DENY_PTRACE("ll_trace_test"),
819 				     "tracee_domain", field, sizeof(field)));
820 		EXPECT_EQ(variant->sandbox_tracee, strcmp("0", field) != 0)
821 		{
822 			TH_LOG("Unexpected tracee_domain=%s", field);
823 		}
824 	} else {
825 		EXPECT_EQ(0, count)
826 		{
827 			TH_LOG("Expected 0 deny_ptrace events, got %d\n%s",
828 			       count, buf);
829 		}
830 	}
831 
832 	free(buf);
833 }
834 
835 TEST_HARNESS_MAIN
836