162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Landlock tests - Ptrace
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
662306a36Sopenharmony_ci * Copyright © 2019-2020 ANSSI
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define _GNU_SOURCE
1062306a36Sopenharmony_ci#include <errno.h>
1162306a36Sopenharmony_ci#include <fcntl.h>
1262306a36Sopenharmony_ci#include <linux/landlock.h>
1362306a36Sopenharmony_ci#include <signal.h>
1462306a36Sopenharmony_ci#include <sys/prctl.h>
1562306a36Sopenharmony_ci#include <sys/ptrace.h>
1662306a36Sopenharmony_ci#include <sys/types.h>
1762306a36Sopenharmony_ci#include <sys/wait.h>
1862306a36Sopenharmony_ci#include <unistd.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "common.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Copied from security/yama/yama_lsm.c */
2362306a36Sopenharmony_ci#define YAMA_SCOPE_DISABLED 0
2462306a36Sopenharmony_ci#define YAMA_SCOPE_RELATIONAL 1
2562306a36Sopenharmony_ci#define YAMA_SCOPE_CAPABILITY 2
2662306a36Sopenharmony_ci#define YAMA_SCOPE_NO_ATTACH 3
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void create_domain(struct __test_metadata *const _metadata)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	int ruleset_fd;
3162306a36Sopenharmony_ci	struct landlock_ruleset_attr ruleset_attr = {
3262306a36Sopenharmony_ci		.handled_access_fs = LANDLOCK_ACCESS_FS_MAKE_BLOCK,
3362306a36Sopenharmony_ci	};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	ruleset_fd =
3662306a36Sopenharmony_ci		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
3762306a36Sopenharmony_ci	EXPECT_LE(0, ruleset_fd)
3862306a36Sopenharmony_ci	{
3962306a36Sopenharmony_ci		TH_LOG("Failed to create a ruleset: %s", strerror(errno));
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
4262306a36Sopenharmony_ci	EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
4362306a36Sopenharmony_ci	EXPECT_EQ(0, close(ruleset_fd));
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int test_ptrace_read(const pid_t pid)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	static const char path_template[] = "/proc/%d/environ";
4962306a36Sopenharmony_ci	char procenv_path[sizeof(path_template) + 10];
5062306a36Sopenharmony_ci	int procenv_path_size, fd;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	procenv_path_size = snprintf(procenv_path, sizeof(procenv_path),
5362306a36Sopenharmony_ci				     path_template, pid);
5462306a36Sopenharmony_ci	if (procenv_path_size >= sizeof(procenv_path))
5562306a36Sopenharmony_ci		return E2BIG;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	fd = open(procenv_path, O_RDONLY | O_CLOEXEC);
5862306a36Sopenharmony_ci	if (fd < 0)
5962306a36Sopenharmony_ci		return errno;
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * Mixing error codes from close(2) and open(2) should not lead to any
6262306a36Sopenharmony_ci	 * (access type) confusion for this test.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	if (close(fd) != 0)
6562306a36Sopenharmony_ci		return errno;
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int get_yama_ptrace_scope(void)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int ret;
7262306a36Sopenharmony_ci	char buf[2] = {};
7362306a36Sopenharmony_ci	const int fd = open("/proc/sys/kernel/yama/ptrace_scope", O_RDONLY);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (fd < 0)
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (read(fd, buf, 1) < 0) {
7962306a36Sopenharmony_ci		close(fd);
8062306a36Sopenharmony_ci		return -1;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	ret = atoi(buf);
8462306a36Sopenharmony_ci	close(fd);
8562306a36Sopenharmony_ci	return ret;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* clang-format off */
8962306a36Sopenharmony_ciFIXTURE(hierarchy) {};
9062306a36Sopenharmony_ci/* clang-format on */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciFIXTURE_VARIANT(hierarchy)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	const bool domain_both;
9562306a36Sopenharmony_ci	const bool domain_parent;
9662306a36Sopenharmony_ci	const bool domain_child;
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Test multiple tracing combinations between a parent process P1 and a child
10162306a36Sopenharmony_ci * process P2.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * Yama's scoped ptrace is presumed disabled.  If enabled, this optional
10462306a36Sopenharmony_ci * restriction is enforced in addition to any Landlock check, which means that
10562306a36Sopenharmony_ci * all P2 requests to trace P1 would be denied.
10662306a36Sopenharmony_ci */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci *        No domain
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci *   P1-.               P1 -> P2 : allow
11262306a36Sopenharmony_ci *       \              P2 -> P1 : allow
11362306a36Sopenharmony_ci *        'P2
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci/* clang-format off */
11662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
11762306a36Sopenharmony_ci	/* clang-format on */
11862306a36Sopenharmony_ci	.domain_both = false,
11962306a36Sopenharmony_ci	.domain_parent = false,
12062306a36Sopenharmony_ci	.domain_child = false,
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/*
12462306a36Sopenharmony_ci *        Child domain
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci *   P1--.              P1 -> P2 : allow
12762306a36Sopenharmony_ci *        \             P2 -> P1 : deny
12862306a36Sopenharmony_ci *        .'-----.
12962306a36Sopenharmony_ci *        |  P2  |
13062306a36Sopenharmony_ci *        '------'
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci/* clang-format off */
13362306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
13462306a36Sopenharmony_ci	/* clang-format on */
13562306a36Sopenharmony_ci	.domain_both = false,
13662306a36Sopenharmony_ci	.domain_parent = false,
13762306a36Sopenharmony_ci	.domain_child = true,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci/*
14162306a36Sopenharmony_ci *        Parent domain
14262306a36Sopenharmony_ci * .------.
14362306a36Sopenharmony_ci * |  P1  --.           P1 -> P2 : deny
14462306a36Sopenharmony_ci * '------'  \          P2 -> P1 : allow
14562306a36Sopenharmony_ci *            '
14662306a36Sopenharmony_ci *            P2
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_ci/* clang-format off */
14962306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
15062306a36Sopenharmony_ci	/* clang-format on */
15162306a36Sopenharmony_ci	.domain_both = false,
15262306a36Sopenharmony_ci	.domain_parent = true,
15362306a36Sopenharmony_ci	.domain_child = false,
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci *        Parent + child domain (siblings)
15862306a36Sopenharmony_ci * .------.
15962306a36Sopenharmony_ci * |  P1  ---.          P1 -> P2 : deny
16062306a36Sopenharmony_ci * '------'   \         P2 -> P1 : deny
16162306a36Sopenharmony_ci *         .---'--.
16262306a36Sopenharmony_ci *         |  P2  |
16362306a36Sopenharmony_ci *         '------'
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ci/* clang-format off */
16662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
16762306a36Sopenharmony_ci	/* clang-format on */
16862306a36Sopenharmony_ci	.domain_both = false,
16962306a36Sopenharmony_ci	.domain_parent = true,
17062306a36Sopenharmony_ci	.domain_child = true,
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*
17462306a36Sopenharmony_ci *         Same domain (inherited)
17562306a36Sopenharmony_ci * .-------------.
17662306a36Sopenharmony_ci * | P1----.     |      P1 -> P2 : allow
17762306a36Sopenharmony_ci * |        \    |      P2 -> P1 : allow
17862306a36Sopenharmony_ci * |         '   |
17962306a36Sopenharmony_ci * |         P2  |
18062306a36Sopenharmony_ci * '-------------'
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_ci/* clang-format off */
18362306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
18462306a36Sopenharmony_ci	/* clang-format on */
18562306a36Sopenharmony_ci	.domain_both = true,
18662306a36Sopenharmony_ci	.domain_parent = false,
18762306a36Sopenharmony_ci	.domain_child = false,
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci *         Inherited + child domain
19262306a36Sopenharmony_ci * .-----------------.
19362306a36Sopenharmony_ci * |  P1----.        |  P1 -> P2 : allow
19462306a36Sopenharmony_ci * |         \       |  P2 -> P1 : deny
19562306a36Sopenharmony_ci * |        .-'----. |
19662306a36Sopenharmony_ci * |        |  P2  | |
19762306a36Sopenharmony_ci * |        '------' |
19862306a36Sopenharmony_ci * '-----------------'
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_ci/* clang-format off */
20162306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
20262306a36Sopenharmony_ci	/* clang-format on */
20362306a36Sopenharmony_ci	.domain_both = true,
20462306a36Sopenharmony_ci	.domain_parent = false,
20562306a36Sopenharmony_ci	.domain_child = true,
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci *         Inherited + parent domain
21062306a36Sopenharmony_ci * .-----------------.
21162306a36Sopenharmony_ci * |.------.         |  P1 -> P2 : deny
21262306a36Sopenharmony_ci * ||  P1  ----.     |  P2 -> P1 : allow
21362306a36Sopenharmony_ci * |'------'    \    |
21462306a36Sopenharmony_ci * |             '   |
21562306a36Sopenharmony_ci * |             P2  |
21662306a36Sopenharmony_ci * '-----------------'
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_ci/* clang-format off */
21962306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
22062306a36Sopenharmony_ci	/* clang-format on */
22162306a36Sopenharmony_ci	.domain_both = true,
22262306a36Sopenharmony_ci	.domain_parent = true,
22362306a36Sopenharmony_ci	.domain_child = false,
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci *         Inherited + parent and child domain (siblings)
22862306a36Sopenharmony_ci * .-----------------.
22962306a36Sopenharmony_ci * | .------.        |  P1 -> P2 : deny
23062306a36Sopenharmony_ci * | |  P1  .        |  P2 -> P1 : deny
23162306a36Sopenharmony_ci * | '------'\       |
23262306a36Sopenharmony_ci * |          \      |
23362306a36Sopenharmony_ci * |        .--'---. |
23462306a36Sopenharmony_ci * |        |  P2  | |
23562306a36Sopenharmony_ci * |        '------' |
23662306a36Sopenharmony_ci * '-----------------'
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_ci/* clang-format off */
23962306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
24062306a36Sopenharmony_ci	/* clang-format on */
24162306a36Sopenharmony_ci	.domain_both = true,
24262306a36Sopenharmony_ci	.domain_parent = true,
24362306a36Sopenharmony_ci	.domain_child = true,
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciFIXTURE_SETUP(hierarchy)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciFIXTURE_TEARDOWN(hierarchy)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
25562306a36Sopenharmony_ciTEST_F(hierarchy, trace)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	pid_t child, parent;
25862306a36Sopenharmony_ci	int status, err_proc_read;
25962306a36Sopenharmony_ci	int pipe_child[2], pipe_parent[2];
26062306a36Sopenharmony_ci	int yama_ptrace_scope;
26162306a36Sopenharmony_ci	char buf_parent;
26262306a36Sopenharmony_ci	long ret;
26362306a36Sopenharmony_ci	bool can_read_child, can_trace_child, can_read_parent, can_trace_parent;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	yama_ptrace_scope = get_yama_ptrace_scope();
26662306a36Sopenharmony_ci	ASSERT_LE(0, yama_ptrace_scope);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (yama_ptrace_scope > YAMA_SCOPE_DISABLED)
26962306a36Sopenharmony_ci		TH_LOG("Incomplete tests due to Yama restrictions (scope %d)",
27062306a36Sopenharmony_ci		       yama_ptrace_scope);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * can_read_child is true if a parent process can read its child
27462306a36Sopenharmony_ci	 * process, which is only the case when the parent process is not
27562306a36Sopenharmony_ci	 * isolated from the child with a dedicated Landlock domain.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	can_read_child = !variant->domain_parent;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * can_trace_child is true if a parent process can trace its child
28162306a36Sopenharmony_ci	 * process.  This depends on two conditions:
28262306a36Sopenharmony_ci	 * - The parent process is not isolated from the child with a dedicated
28362306a36Sopenharmony_ci	 *   Landlock domain.
28462306a36Sopenharmony_ci	 * - Yama allows tracing children (up to YAMA_SCOPE_RELATIONAL).
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	can_trace_child = can_read_child &&
28762306a36Sopenharmony_ci			  yama_ptrace_scope <= YAMA_SCOPE_RELATIONAL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/*
29062306a36Sopenharmony_ci	 * can_read_parent is true if a child process can read its parent
29162306a36Sopenharmony_ci	 * process, which is only the case when the child process is not
29262306a36Sopenharmony_ci	 * isolated from the parent with a dedicated Landlock domain.
29362306a36Sopenharmony_ci	 */
29462306a36Sopenharmony_ci	can_read_parent = !variant->domain_child;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/*
29762306a36Sopenharmony_ci	 * can_trace_parent is true if a child process can trace its parent
29862306a36Sopenharmony_ci	 * process.  This depends on two conditions:
29962306a36Sopenharmony_ci	 * - The child process is not isolated from the parent with a dedicated
30062306a36Sopenharmony_ci	 *   Landlock domain.
30162306a36Sopenharmony_ci	 * - Yama is disabled (YAMA_SCOPE_DISABLED).
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci	can_trace_parent = can_read_parent &&
30462306a36Sopenharmony_ci			   yama_ptrace_scope <= YAMA_SCOPE_DISABLED;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * Removes all effective and permitted capabilities to not interfere
30862306a36Sopenharmony_ci	 * with cap_ptrace_access_check() in case of PTRACE_MODE_FSCREDS.
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	drop_caps(_metadata);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	parent = getpid();
31362306a36Sopenharmony_ci	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
31462306a36Sopenharmony_ci	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
31562306a36Sopenharmony_ci	if (variant->domain_both) {
31662306a36Sopenharmony_ci		create_domain(_metadata);
31762306a36Sopenharmony_ci		if (!_metadata->passed)
31862306a36Sopenharmony_ci			/* Aborts before forking. */
31962306a36Sopenharmony_ci			return;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	child = fork();
32362306a36Sopenharmony_ci	ASSERT_LE(0, child);
32462306a36Sopenharmony_ci	if (child == 0) {
32562306a36Sopenharmony_ci		char buf_child;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci		ASSERT_EQ(0, close(pipe_parent[1]));
32862306a36Sopenharmony_ci		ASSERT_EQ(0, close(pipe_child[0]));
32962306a36Sopenharmony_ci		if (variant->domain_child)
33062306a36Sopenharmony_ci			create_domain(_metadata);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		/* Waits for the parent to be in a domain, if any. */
33362306a36Sopenharmony_ci		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		/* Tests PTRACE_MODE_READ on the parent. */
33662306a36Sopenharmony_ci		err_proc_read = test_ptrace_read(parent);
33762306a36Sopenharmony_ci		if (can_read_parent) {
33862306a36Sopenharmony_ci			EXPECT_EQ(0, err_proc_read);
33962306a36Sopenharmony_ci		} else {
34062306a36Sopenharmony_ci			EXPECT_EQ(EACCES, err_proc_read);
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		/* Tests PTRACE_ATTACH on the parent. */
34462306a36Sopenharmony_ci		ret = ptrace(PTRACE_ATTACH, parent, NULL, 0);
34562306a36Sopenharmony_ci		if (can_trace_parent) {
34662306a36Sopenharmony_ci			EXPECT_EQ(0, ret);
34762306a36Sopenharmony_ci		} else {
34862306a36Sopenharmony_ci			EXPECT_EQ(-1, ret);
34962306a36Sopenharmony_ci			EXPECT_EQ(EPERM, errno);
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		if (ret == 0) {
35262306a36Sopenharmony_ci			ASSERT_EQ(parent, waitpid(parent, &status, 0));
35362306a36Sopenharmony_ci			ASSERT_EQ(1, WIFSTOPPED(status));
35462306a36Sopenharmony_ci			ASSERT_EQ(0, ptrace(PTRACE_DETACH, parent, NULL, 0));
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		/* Tests child PTRACE_TRACEME. */
35862306a36Sopenharmony_ci		ret = ptrace(PTRACE_TRACEME);
35962306a36Sopenharmony_ci		if (can_trace_child) {
36062306a36Sopenharmony_ci			EXPECT_EQ(0, ret);
36162306a36Sopenharmony_ci		} else {
36262306a36Sopenharmony_ci			EXPECT_EQ(-1, ret);
36362306a36Sopenharmony_ci			EXPECT_EQ(EPERM, errno);
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/*
36762306a36Sopenharmony_ci		 * Signals that the PTRACE_ATTACH test is done and the
36862306a36Sopenharmony_ci		 * PTRACE_TRACEME test is ongoing.
36962306a36Sopenharmony_ci		 */
37062306a36Sopenharmony_ci		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		if (can_trace_child) {
37362306a36Sopenharmony_ci			ASSERT_EQ(0, raise(SIGSTOP));
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		/* Waits for the parent PTRACE_ATTACH test. */
37762306a36Sopenharmony_ci		ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
37862306a36Sopenharmony_ci		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
37962306a36Sopenharmony_ci		return;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ASSERT_EQ(0, close(pipe_child[1]));
38362306a36Sopenharmony_ci	ASSERT_EQ(0, close(pipe_parent[0]));
38462306a36Sopenharmony_ci	if (variant->domain_parent)
38562306a36Sopenharmony_ci		create_domain(_metadata);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* Signals that the parent is in a domain, if any. */
38862306a36Sopenharmony_ci	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/*
39162306a36Sopenharmony_ci	 * Waits for the child to test PTRACE_ATTACH on the parent and start
39262306a36Sopenharmony_ci	 * testing PTRACE_TRACEME.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Tests child PTRACE_TRACEME. */
39762306a36Sopenharmony_ci	if (can_trace_child) {
39862306a36Sopenharmony_ci		ASSERT_EQ(child, waitpid(child, &status, 0));
39962306a36Sopenharmony_ci		ASSERT_EQ(1, WIFSTOPPED(status));
40062306a36Sopenharmony_ci		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
40162306a36Sopenharmony_ci	} else {
40262306a36Sopenharmony_ci		/* The child should not be traced by the parent. */
40362306a36Sopenharmony_ci		EXPECT_EQ(-1, ptrace(PTRACE_DETACH, child, NULL, 0));
40462306a36Sopenharmony_ci		EXPECT_EQ(ESRCH, errno);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Tests PTRACE_MODE_READ on the child. */
40862306a36Sopenharmony_ci	err_proc_read = test_ptrace_read(child);
40962306a36Sopenharmony_ci	if (can_read_child) {
41062306a36Sopenharmony_ci		EXPECT_EQ(0, err_proc_read);
41162306a36Sopenharmony_ci	} else {
41262306a36Sopenharmony_ci		EXPECT_EQ(EACCES, err_proc_read);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* Tests PTRACE_ATTACH on the child. */
41662306a36Sopenharmony_ci	ret = ptrace(PTRACE_ATTACH, child, NULL, 0);
41762306a36Sopenharmony_ci	if (can_trace_child) {
41862306a36Sopenharmony_ci		EXPECT_EQ(0, ret);
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		EXPECT_EQ(-1, ret);
42162306a36Sopenharmony_ci		EXPECT_EQ(EPERM, errno);
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (ret == 0) {
42562306a36Sopenharmony_ci		ASSERT_EQ(child, waitpid(child, &status, 0));
42662306a36Sopenharmony_ci		ASSERT_EQ(1, WIFSTOPPED(status));
42762306a36Sopenharmony_ci		ASSERT_EQ(0, ptrace(PTRACE_DETACH, child, NULL, 0));
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Signals that the parent PTRACE_ATTACH test is done. */
43162306a36Sopenharmony_ci	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
43262306a36Sopenharmony_ci	ASSERT_EQ(child, waitpid(child, &status, 0));
43362306a36Sopenharmony_ci	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
43462306a36Sopenharmony_ci	    WEXITSTATUS(status) != EXIT_SUCCESS)
43562306a36Sopenharmony_ci		_metadata->passed = 0;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciTEST_HARNESS_MAIN
439