File Index
Files and directories
Sign in
FILE

ptrace_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/landlock/ptrace_test.c C
  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 <signal.h>
 15#include <sys/prctl.h>
 16#include <sys/ptrace.h>
 17#include <sys/types.h>
 18#include <sys/wait.h>
 19#include <unistd.h>
 20
 21#include "audit.h"
 22#include "common.h"
 23
 24/* Copied from security/yama/yama_lsm.c */
 25#define YAMA_SCOPE_DISABLED 0
 26#define YAMA_SCOPE_RELATIONAL 1
 27
 28static void create_domain(struct __test_metadata *const _metadata)
 29{
 30	int ruleset_fd;
 31	struct landlock_ruleset_attr ruleset_attr = {
 32		.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
 33	};
 34
 35	ruleset_fd =
 36		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
 37	EXPECT_LE(0, ruleset_fd)
 38	{
 39		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
 40	}
 41	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
 42	EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
 43	EXPECT_EQ(0, close(ruleset_fd));
 44}
 45
 46static int test_ptrace_read(const pid_t pid)
 47{
 48	static const char path_template[] = "/proc/%d/environ";
 49	char procenv_path[sizeof(path_template) + 10];
 50	int procenv_path_size, fd;
 51
 52	procenv_path_size = snprintf(procenv_path, sizeof(procenv_path),
 53				     path_template, pid);
 54	if (procenv_path_size >= sizeof(procenv_path))
 55		return E2BIG;
 56
 57	fd = open(procenv_path, O_RDONLY | O_CLOEXEC);
 58	if (fd < 0)
 59		return errno;
 60	/*
 61	 * Mixing error codes from close(2) and open(2) should not lead to any
 62	 * (access type) confusion for this test.
 63	 */
 64	if (close(fd) != 0)
 65		return errno;
 66	return 0;
 67}
 68
 69static int get_yama_ptrace_scope(void)
 70{
 71	int ret;
 72	char buf[2] = {};
 73	const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
 74
 75	if (fd < 0)
 76		return 0;
 77
 78	if (read(fd, buf, 1) < 0) {
 79		close(fd);
 80		return -1;
 81	}
 82
 83	ret = atoi(buf);
 84	close(fd);
 85	return ret;
 86}
 87
 88/* clang-format off */
 89FIXTURE(scoped_domains) {};
 90/* clang-format on */
 91
 92/*
 93 * Test multiple tracing combinations between a parent process P1 and a child
 94 * process P2.
 95 *
 96 * Yama's scoped ptrace is presumed disabled.  If enabled, this optional
 97 * restriction is enforced in addition to any Landlock check, which means that
 98 * all P2 requests to trace P1 would be denied.
 99 */
100#include "scoped_base_variants.h"
101
102FIXTURE_SETUP(scoped_domains)
103{
104}
105
106FIXTURE_TEARDOWN(scoped_domains)
107{
108}
109
110/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
111TEST_F(scoped_domains, trace)
112{
113	pid_t child, parent;
114	int status, err_proc_read;
115	int pipe_child[2], pipe_parent[2];
116	int yama_ptrace_scope;
117	char buf_parent;
118	long ret;
119	bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
120
121	yama_ptrace_scope = get_yama_ptrace_scope();
122	ASSERT_LE(0, yama_ptrace_scope);
123
124	if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
125		TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
126		       yama_ptrace_scope);
127
128	/*
129	 * can_read_child is true if a parent process can read its child
130	 * process, which is only the case when the parent process is not
131	 * isolated from the child with a dedicated Landlock domain.
132	 */
133	can_read_child = !variant->domain_parent;
134
135	/*
136	 * can_trace_child is true if a parent process can trace its child
137	 * process.  This depends on two conditions:
138	 * - The parent process is not isolated from the child with a dedicated
139	 *   Landlock domain.
140	 * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
141	 */
142	can_trace_child = can_read_child &&
143			  yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
144
145	/*
146	 * can_read_parent is true if a child process can read its parent
147	 * process, which is only the case when the child process is not
148	 * isolated from the parent with a dedicated Landlock domain.
149	 */
150	can_read_parent = !variant->domain_child;
151
152	/*
153	 * can_trace_parent is true if a child process can trace its parent
154	 * process.  This depends on two conditions:
155	 * - The child process is not isolated from the parent with a dedicated
156	 *   Landlock domain.
157	 * - Yama is disabled (YAMA_SCOPE_DISABLED).
158	 */
159	can_trace_parent = can_read_parent &&
160			   yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
161
162	/*
163	 * Removes all effective and permitted capabilities to not interfere
164	 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
165	 */
166	drop_caps(_metadata);
167
168	parent = getpid();
169	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
170	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
171	if (variant->domain_both) {
172		create_domain(_metadata);
173		if (!__test_passed(_metadata))
174			/* Aborts before forking. */
175			return;
176	}
177
178	child = fork();
179	ASSERT_LE(0, child);
180	if (child == 0) {
181		char buf_child;
182
183		ASSERT_EQ(0, close(pipe_parent[1]));
184		ASSERT_EQ(0, close(pipe_child[0]));
185		if (variant->domain_child)
186			create_domain(_metadata);
187
188		/* Waits for the parent to be in a domain, if any. */
189		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
190
191		/* Tests PTRACE_MODE_READ on the parent. */
192		err_proc_read = test_ptrace_read(parent);
193		if (can_read_parent) {
194			EXPECT_EQ(0, err_proc_read);
195		} else {
196			EXPECT_EQ(EACCES, err_proc_read);
197		}
198
199		/* Tests PTRACE_ATTACH on the parent. */
200		ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
201		if (can_trace_parent) {
202			EXPECT_EQ(0, ret);
203		} else {
204			EXPECT_EQ(-1, ret);
205			EXPECT_EQ(EPERM, errno);
206		}
207		if (ret == 0) {
208			ASSERT_EQ(parent, waitpid(parent, &status, 0));
209			ASSERT_EQ(1, WIFSTOPPED(status));
210			ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0));
211		}
212
213		/* Tests child PTRACE_TRACEME. */
214		ret = ptrace(PTRACE_TRACEME);
215		if (can_trace_child) {
216			EXPECT_EQ(0, ret);
217		} else {
218			EXPECT_EQ(-1, ret);
219			EXPECT_EQ(EPERM, errno);
220		}
221
222		/*
223		 * Signals that the PTRACE_ATTACH test is done and the
224		 * PTRACE_TRACEME test is ongoing.
225		 */
226		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
227
228		if (can_trace_child) {
229			ASSERT_EQ(0, raise(SIGSTOP));
230		}
231
232		/* Waits for the parent PTRACE_ATTACH test. */
233		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
234		_exit(_metadata->exit_code);
235		return;
236	}
237
238	ASSERT_EQ(0, close(pipe_child[1]));
239	ASSERT_EQ(0, close(pipe_parent[0]));
240	if (variant->domain_parent)
241		create_domain(_metadata);
242
243	/* Signals that the parent is in a domain, if any. */
244	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
245
246	/*
247	 * Waits for the child to test PTRACE_ATTACH on the parent and start
248	 * testing PTRACE_TRACEME.
249	 */
250	ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
251
252	/* Tests child PTRACE_TRACEME. */
253	if (can_trace_child) {
254		ASSERT_EQ(child, waitpid(child, &status, 0));
255		ASSERT_EQ(1, WIFSTOPPED(status));
256		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
257	} else {
258		/* The child should not be traced by the parent. */
259		EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
260		EXPECT_EQ(ESRCH, errno);
261	}
262
263	/* Tests PTRACE_MODE_READ on the child. */
264	err_proc_read = test_ptrace_read(child);
265	if (can_read_child) {
266		EXPECT_EQ(0, err_proc_read);
267	} else {
268		EXPECT_EQ(EACCES, err_proc_read);
269	}
270
271	/* Tests PTRACE_ATTACH on the child. */
272	ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
273	if (can_trace_child) {
274		EXPECT_EQ(0, ret);
275	} else {
276		EXPECT_EQ(-1, ret);
277		EXPECT_EQ(EPERM, errno);
278	}
279
280	if (ret == 0) {
281		ASSERT_EQ(child, waitpid(child, &status, 0));
282		ASSERT_EQ(1, WIFSTOPPED(status));
283		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
284	}
285
286	/* Signals that the parent PTRACE_ATTACH test is done. */
287	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
288	ASSERT_EQ(child, waitpid(child, &status, 0));
289
290	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
291	    WEXITSTATUS(status) != EXIT_SUCCESS)
292		_metadata->exit_code = KSFT_FAIL;
293}
294
295static int matches_log_ptrace(struct __test_metadata *const _metadata,
296			      int audit_fd, const pid_t opid)
297{
298	static const char log_template[] = REGEX_LANDLOCK_PREFIX
299		" blockers=ptrace opid=%d ocomm=\"ptrace_test\"$";
300	char log_match[sizeof(log_template) + 10];
301	int log_match_len;
302
303	log_match_len =
304		snprintf(log_match, sizeof(log_match), log_template, opid);
305	if (log_match_len > sizeof(log_match))
306		return -E2BIG;
307
308	return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
309				  NULL);
310}
311
312FIXTURE(audit)
313{
314	struct audit_filter audit_filter;
315	int audit_fd;
316};
317
318FIXTURE_SETUP(audit)
319{
320	disable_caps(_metadata);
321	set_cap(_metadata, CAP_AUDIT_CONTROL);
322	self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
323	EXPECT_LE(0, self->audit_fd);
324	clear_cap(_metadata, CAP_AUDIT_CONTROL);
325}
326
327FIXTURE_TEARDOWN_PARENT(audit)
328{
329	EXPECT_EQ(0, audit_cleanup(-1, NULL));
330}
331
332/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
333TEST_F(audit, trace)
334{
335	pid_t child;
336	int status;
337	int pipe_child[2], pipe_parent[2];
338	int yama_ptrace_scope;
339	char buf_parent;
340	struct audit_records records;
341
342	/* Makes sure there is no superfluous logged records. */
343	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
344	EXPECT_EQ(0, records.access);
345	EXPECT_EQ(0, records.domain);
346
347	yama_ptrace_scope = get_yama_ptrace_scope();
348	ASSERT_LE(0, yama_ptrace_scope);
349
350	if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
351		TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
352		       yama_ptrace_scope);
353
354	/*
355	 * Removes all effective and permitted capabilities to not interfere
356	 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
357	 */
358	drop_caps(_metadata);
359
360	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
361	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
362
363	child = fork();
364	ASSERT_LE(0, child);
365	if (child == 0) {
366		char buf_child;
367
368		ASSERT_EQ(0, close(pipe_parent[1]));
369		ASSERT_EQ(0, close(pipe_child[0]));
370
371		/* Waits for the parent to be in a domain, if any. */
372		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
373
374		/* Tests child PTRACE_TRACEME. */
375		EXPECT_EQ(-1, ptrace(PTRACE_TRACEME));
376		EXPECT_EQ(EPERM, errno);
377		/* We should see the child process. */
378		EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd,
379						getpid()));
380
381		EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
382		EXPECT_EQ(0, records.access);
383		/* Checks for a domain creation. */
384		EXPECT_EQ(1, records.domain);
385
386		/*
387		 * Signals that the PTRACE_ATTACH test is done and the
388		 * PTRACE_TRACEME test is ongoing.
389		 */
390		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
391
392		/* Waits for the parent PTRACE_ATTACH test. */
393		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
394		_exit(_metadata->exit_code);
395		return;
396	}
397
398	ASSERT_EQ(0, close(pipe_child[1]));
399	ASSERT_EQ(0, close(pipe_parent[0]));
400	create_domain(_metadata);
401
402	/* Signals that the parent is in a domain. */
403	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
404
405	/*
406	 * Waits for the child to test PTRACE_ATTACH on the parent and start
407	 * testing PTRACE_TRACEME.
408	 */
409	ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
410
411	/* The child should not be traced by the parent. */
412	EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
413	EXPECT_EQ(ESRCH, errno);
414
415	/* Tests PTRACE_ATTACH on the child. */
416	EXPECT_EQ(-1, ptrace(PTRACE_ATTACH, child, NULL, 0));
417	EXPECT_EQ(EPERM, errno);
418	EXPECT_EQ(0, matches_log_ptrace(_metadata, self->audit_fd, child));
419
420	/* Signals that the parent PTRACE_ATTACH test is done. */
421	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
422	ASSERT_EQ(child, waitpid(child, &status, 0));
423	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
424	    WEXITSTATUS(status) != EXIT_SUCCESS)
425		_metadata->exit_code = KSFT_FAIL;
426
427	/* Makes sure there is no superfluous logged records. */
428	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
429	EXPECT_EQ(0, records.access);
430	EXPECT_EQ(0, records.domain);
431}
432
433TEST_HARNESS_MAIN
~/ Read-only file preview.