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
create_domain(struct __test_metadata * const _metadata)28 static 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
test_ptrace_read(const pid_t pid)46 static 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
get_yama_ptrace_scope(void)69 static 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 */
FIXTURE(scoped_domains)89 FIXTURE(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
FIXTURE_SETUP(scoped_domains)102 FIXTURE_SETUP(scoped_domains)
103 {
104 }
105
FIXTURE_TEARDOWN(scoped_domains)106 FIXTURE_TEARDOWN(scoped_domains)
107 {
108 }
109
110 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
TEST_F(scoped_domains,trace)111 TEST_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
matches_log_ptrace(struct __test_metadata * const _metadata,int audit_fd,const pid_t opid)295 static 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
FIXTURE(audit)312 FIXTURE(audit)
313 {
314 struct audit_filter audit_filter;
315 int audit_fd;
316 };
317
FIXTURE_SETUP(audit)318 FIXTURE_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
FIXTURE_TEARDOWN_PARENT(audit)327 FIXTURE_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. */
TEST_F(audit,trace)333 TEST_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
433 TEST_HARNESS_MAIN
434