Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / testing / selftests / landlock / ptrace_test.c
bloba19db4d0b3bdc5ca54b015f85fc8fdff9fd0b1f9
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Landlock tests - Ptrace
5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
6 * Copyright © 2019-2020 ANSSI
7 */
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <linux/landlock.h>
13 #include <signal.h>
14 #include <sys/prctl.h>
15 #include <sys/ptrace.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
20 #include "common.h"
22 /* Copied from security/yama/yama_lsm.c */
23 #define YAMA_SCOPE_DISABLED 0
24 #define YAMA_SCOPE_RELATIONAL 1
25 #define YAMA_SCOPE_CAPABILITY 2
26 #define YAMA_SCOPE_NO_ATTACH 3
28 static void create_domain(struct __test_metadata *const _metadata)
30 int ruleset_fd;
31 struct landlock_ruleset_attr ruleset_attr = {
32 .handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
35 ruleset_fd =
36 landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
37 EXPECT_LE(0, ruleset_fd)
39 TH_LOG("Failed to create a ruleset: %s", strerror(errno));
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));
46 static int test_ptrace_read(const pid_t pid)
48 static const char path_template[] = "/proc/%d/environ";
49 char procenv_path[sizeof(path_template) + 10];
50 int procenv_path_size, fd;
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;
57 fd = open(procenv_path, O_RDONLY | O_CLOEXEC);
58 if (fd < 0)
59 return errno;
61 * Mixing error codes from close(2) and open(2) should not lead to any
62 * (access type) confusion for this test.
64 if (close(fd) != 0)
65 return errno;
66 return 0;
69 static int get_yama_ptrace_scope(void)
71 int ret;
72 char buf[2] = {};
73 const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
75 if (fd < 0)
76 return 0;
78 if (read(fd, buf, 1) < 0) {
79 close(fd);
80 return -1;
83 ret = atoi(buf);
84 close(fd);
85 return ret;
88 /* clang-format off */
89 FIXTURE(hierarchy) {};
90 /* clang-format on */
92 FIXTURE_VARIANT(hierarchy)
94 const bool domain_both;
95 const bool domain_parent;
96 const bool domain_child;
100 * Test multiple tracing combinations between a parent process P1 and a child
101 * process P2.
103 * Yama's scoped ptrace is presumed disabled. If enabled, this optional
104 * restriction is enforced in addition to any Landlock check, which means that
105 * all P2 requests to trace P1 would be denied.
109 * No domain
111 * P1-. P1 -> P2 : allow
112 * \ P2 -> P1 : allow
113 * 'P2
115 /* clang-format off */
116 FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
117 /* clang-format on */
118 .domain_both = false,
119 .domain_parent = false,
120 .domain_child = false,
124 * Child domain
126 * P1--. P1 -> P2 : allow
127 * \ P2 -> P1 : deny
128 * .'-----.
129 * | P2 |
130 * '------'
132 /* clang-format off */
133 FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
134 /* clang-format on */
135 .domain_both = false,
136 .domain_parent = false,
137 .domain_child = true,
141 * Parent domain
142 * .------.
143 * | P1 --. P1 -> P2 : deny
144 * '------' \ P2 -> P1 : allow
146 * P2
148 /* clang-format off */
149 FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
150 /* clang-format on */
151 .domain_both = false,
152 .domain_parent = true,
153 .domain_child = false,
157 * Parent + child domain (siblings)
158 * .------.
159 * | P1 ---. P1 -> P2 : deny
160 * '------' \ P2 -> P1 : deny
161 * .---'--.
162 * | P2 |
163 * '------'
165 /* clang-format off */
166 FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
167 /* clang-format on */
168 .domain_both = false,
169 .domain_parent = true,
170 .domain_child = true,
174 * Same domain (inherited)
175 * .-------------.
176 * | P1----. | P1 -> P2 : allow
177 * | \ | P2 -> P1 : allow
178 * | ' |
179 * | P2 |
180 * '-------------'
182 /* clang-format off */
183 FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
184 /* clang-format on */
185 .domain_both = true,
186 .domain_parent = false,
187 .domain_child = false,
191 * Inherited + child domain
192 * .-----------------.
193 * | P1----. | P1 -> P2 : allow
194 * | \ | P2 -> P1 : deny
195 * | .-'----. |
196 * | | P2 | |
197 * | '------' |
198 * '-----------------'
200 /* clang-format off */
201 FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
202 /* clang-format on */
203 .domain_both = true,
204 .domain_parent = false,
205 .domain_child = true,
209 * Inherited + parent domain
210 * .-----------------.
211 * |.------. | P1 -> P2 : deny
212 * || P1 ----. | P2 -> P1 : allow
213 * |'------' \ |
214 * | ' |
215 * | P2 |
216 * '-----------------'
218 /* clang-format off */
219 FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
220 /* clang-format on */
221 .domain_both = true,
222 .domain_parent = true,
223 .domain_child = false,
227 * Inherited + parent and child domain (siblings)
228 * .-----------------.
229 * | .------. | P1 -> P2 : deny
230 * | | P1 . | P2 -> P1 : deny
231 * | '------'\ |
232 * | \ |
233 * | .--'---. |
234 * | | P2 | |
235 * | '------' |
236 * '-----------------'
238 /* clang-format off */
239 FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
240 /* clang-format on */
241 .domain_both = true,
242 .domain_parent = true,
243 .domain_child = true,
246 FIXTURE_SETUP(hierarchy)
250 FIXTURE_TEARDOWN(hierarchy)
254 /* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
255 TEST_F(hierarchy, trace)
257 pid_t child, parent;
258 int status, err_proc_read;
259 int pipe_child[2], pipe_parent[2];
260 int yama_ptrace_scope;
261 char buf_parent;
262 long ret;
263 bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
265 yama_ptrace_scope = get_yama_ptrace_scope();
266 ASSERT_LE(0, yama_ptrace_scope);
268 if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
269 TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
270 yama_ptrace_scope);
273 * can_read_child is true if a parent process can read its child
274 * process, which is only the case when the parent process is not
275 * isolated from the child with a dedicated Landlock domain.
277 can_read_child = !variant->domain_parent;
280 * can_trace_child is true if a parent process can trace its child
281 * process. This depends on two conditions:
282 * - The parent process is not isolated from the child with a dedicated
283 * Landlock domain.
284 * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
286 can_trace_child = can_read_child &&
287 yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
290 * can_read_parent is true if a child process can read its parent
291 * process, which is only the case when the child process is not
292 * isolated from the parent with a dedicated Landlock domain.
294 can_read_parent = !variant->domain_child;
297 * can_trace_parent is true if a child process can trace its parent
298 * process. This depends on two conditions:
299 * - The child process is not isolated from the parent with a dedicated
300 * Landlock domain.
301 * - Yama is disabled (YAMA_SCOPE_DISABLED).
303 can_trace_parent = can_read_parent &&
304 yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
307 * Removes all effective and permitted capabilities to not interfere
308 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
310 drop_caps(_metadata);
312 parent = getpid();
313 ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
314 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
315 if (variant->domain_both) {
316 create_domain(_metadata);
317 if (!__test_passed(_metadata))
318 /* Aborts before forking. */
319 return;
322 child = fork();
323 ASSERT_LE(0, child);
324 if (child == 0) {
325 char buf_child;
327 ASSERT_EQ(0, close(pipe_parent[1]));
328 ASSERT_EQ(0, close(pipe_child[0]));
329 if (variant->domain_child)
330 create_domain(_metadata);
332 /* Waits for the parent to be in a domain, if any. */
333 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
335 /* Tests PTRACE_MODE_READ on the parent. */
336 err_proc_read = test_ptrace_read(parent);
337 if (can_read_parent) {
338 EXPECT_EQ(0, err_proc_read);
339 } else {
340 EXPECT_EQ(EACCES, err_proc_read);
343 /* Tests PTRACE_ATTACH on the parent. */
344 ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
345 if (can_trace_parent) {
346 EXPECT_EQ(0, ret);
347 } else {
348 EXPECT_EQ(-1, ret);
349 EXPECT_EQ(EPERM, errno);
351 if (ret == 0) {
352 ASSERT_EQ(parent, waitpid(parent, &status, 0));
353 ASSERT_EQ(1, WIFSTOPPED(status));
354 ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0));
357 /* Tests child PTRACE_TRACEME. */
358 ret = ptrace(PTRACE_TRACEME);
359 if (can_trace_child) {
360 EXPECT_EQ(0, ret);
361 } else {
362 EXPECT_EQ(-1, ret);
363 EXPECT_EQ(EPERM, errno);
367 * Signals that the PTRACE_ATTACH test is done and the
368 * PTRACE_TRACEME test is ongoing.
370 ASSERT_EQ(1, write(pipe_child[1], ".", 1));
372 if (can_trace_child) {
373 ASSERT_EQ(0, raise(SIGSTOP));
376 /* Waits for the parent PTRACE_ATTACH test. */
377 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
378 _exit(_metadata->exit_code);
379 return;
382 ASSERT_EQ(0, close(pipe_child[1]));
383 ASSERT_EQ(0, close(pipe_parent[0]));
384 if (variant->domain_parent)
385 create_domain(_metadata);
387 /* Signals that the parent is in a domain, if any. */
388 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
391 * Waits for the child to test PTRACE_ATTACH on the parent and start
392 * testing PTRACE_TRACEME.
394 ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
396 /* Tests child PTRACE_TRACEME. */
397 if (can_trace_child) {
398 ASSERT_EQ(child, waitpid(child, &status, 0));
399 ASSERT_EQ(1, WIFSTOPPED(status));
400 ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
401 } else {
402 /* The child should not be traced by the parent. */
403 EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
404 EXPECT_EQ(ESRCH, errno);
407 /* Tests PTRACE_MODE_READ on the child. */
408 err_proc_read = test_ptrace_read(child);
409 if (can_read_child) {
410 EXPECT_EQ(0, err_proc_read);
411 } else {
412 EXPECT_EQ(EACCES, err_proc_read);
415 /* Tests PTRACE_ATTACH on the child. */
416 ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
417 if (can_trace_child) {
418 EXPECT_EQ(0, ret);
419 } else {
420 EXPECT_EQ(-1, ret);
421 EXPECT_EQ(EPERM, errno);
424 if (ret == 0) {
425 ASSERT_EQ(child, waitpid(child, &status, 0));
426 ASSERT_EQ(1, WIFSTOPPED(status));
427 ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
430 /* Signals that the parent PTRACE_ATTACH test is done. */
431 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
432 ASSERT_EQ(child, waitpid(child, &status, 0));
434 if (WIFSIGNALED(status) || !WIFEXITED(status) ||
435 WEXITSTATUS(status) != EXIT_SUCCESS)
436 _metadata->exit_code = KSFT_FAIL;
439 TEST_HARNESS_MAIN