1*00831369Sandvar /* $NetBSD: t_ptrace.c,v 1.5 2022/05/24 20:08:38 andvar Exp $ */
224e714f4Skamil
324e714f4Skamil /*-
424e714f4Skamil * Copyright (c) 2016 The NetBSD Foundation, Inc.
524e714f4Skamil * All rights reserved.
624e714f4Skamil *
724e714f4Skamil * Redistribution and use in source and binary forms, with or without
824e714f4Skamil * modification, are permitted provided that the following conditions
924e714f4Skamil * are met:
1024e714f4Skamil * 1. Redistributions of source code must retain the above copyright
1124e714f4Skamil * notice, this list of conditions and the following disclaimer.
1224e714f4Skamil * 2. Redistributions in binary form must reproduce the above copyright
1324e714f4Skamil * notice, this list of conditions and the following disclaimer in the
1424e714f4Skamil * documentation and/or other materials provided with the distribution.
1524e714f4Skamil *
1624e714f4Skamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1724e714f4Skamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1824e714f4Skamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1924e714f4Skamil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2024e714f4Skamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2124e714f4Skamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2224e714f4Skamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2324e714f4Skamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2424e714f4Skamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2524e714f4Skamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2624e714f4Skamil * POSSIBILITY OF SUCH DAMAGE.
2724e714f4Skamil */
2824e714f4Skamil
2924e714f4Skamil #include <sys/cdefs.h>
30*00831369Sandvar __RCSID("$NetBSD: t_ptrace.c,v 1.5 2022/05/24 20:08:38 andvar Exp $");
3124e714f4Skamil
3224e714f4Skamil #include <sys/param.h>
3324e714f4Skamil #include <sys/types.h>
3424e714f4Skamil #include <sys/ptrace.h>
3524e714f4Skamil #include <sys/stat.h>
3624e714f4Skamil #include <sys/sysctl.h>
3724e714f4Skamil #include <err.h>
3824e714f4Skamil #include <errno.h>
3924e714f4Skamil #include <unistd.h>
4024e714f4Skamil
4124e714f4Skamil #include <atf-c.h>
4224e714f4Skamil
4324e714f4Skamil #include "h_macros.h"
4424e714f4Skamil
4524e714f4Skamil /*
4624e714f4Skamil * A child process cannot call atf functions and expect them to magically
4724e714f4Skamil * work like in the parent.
4824e714f4Skamil * The printf(3) messaging from a child will not work out of the box as well
49*00831369Sandvar * without establishing a communication protocol with its parent. To not
5024e714f4Skamil * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
5124e714f4Skamil * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
5224e714f4Skamil */
5324e714f4Skamil #define FORKEE_ASSERTX(x) \
5424e714f4Skamil do { \
5524e714f4Skamil int ret = (x); \
5624e714f4Skamil if (!ret) \
5724e714f4Skamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
5824e714f4Skamil __FILE__, __LINE__, __func__, #x); \
5924e714f4Skamil } while (0)
6024e714f4Skamil
6124e714f4Skamil #define FORKEE_ASSERT(x) \
6224e714f4Skamil do { \
6324e714f4Skamil int ret = (x); \
6424e714f4Skamil if (!ret) \
6524e714f4Skamil err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
6624e714f4Skamil __FILE__, __LINE__, __func__, #x); \
6724e714f4Skamil } while (0)
6824e714f4Skamil
6924e714f4Skamil ATF_TC(attach_pid0);
ATF_TC_HEAD(attach_pid0,tc)7024e714f4Skamil ATF_TC_HEAD(attach_pid0, tc)
7124e714f4Skamil {
7224e714f4Skamil atf_tc_set_md_var(tc, "descr",
7324e714f4Skamil "Assert that a debugger cannot attach to PID 0");
7424e714f4Skamil }
7524e714f4Skamil
ATF_TC_BODY(attach_pid0,tc)7624e714f4Skamil ATF_TC_BODY(attach_pid0, tc)
7724e714f4Skamil {
7824e714f4Skamil errno = 0;
7924e714f4Skamil ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1);
8024e714f4Skamil }
8124e714f4Skamil
8224e714f4Skamil ATF_TC(attach_pid1);
ATF_TC_HEAD(attach_pid1,tc)8324e714f4Skamil ATF_TC_HEAD(attach_pid1, tc)
8424e714f4Skamil {
8524e714f4Skamil atf_tc_set_md_var(tc, "descr",
8624e714f4Skamil "Assert that a debugger cannot attach to PID 1 (as non-root)");
8724e714f4Skamil
8824e714f4Skamil atf_tc_set_md_var(tc, "require.user", "unprivileged");
8924e714f4Skamil }
9024e714f4Skamil
ATF_TC_BODY(attach_pid1,tc)9124e714f4Skamil ATF_TC_BODY(attach_pid1, tc)
9224e714f4Skamil {
9324e714f4Skamil ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
9424e714f4Skamil }
9524e714f4Skamil
9624e714f4Skamil ATF_TC(attach_pid1_securelevel);
ATF_TC_HEAD(attach_pid1_securelevel,tc)9724e714f4Skamil ATF_TC_HEAD(attach_pid1_securelevel, tc)
9824e714f4Skamil {
9924e714f4Skamil atf_tc_set_md_var(tc, "descr",
10024e714f4Skamil "Assert that a debugger cannot attach to PID 1 with "
10124e714f4Skamil "securelevel >= 0 (as root)");
10224e714f4Skamil
10324e714f4Skamil atf_tc_set_md_var(tc, "require.user", "root");
10424e714f4Skamil }
10524e714f4Skamil
ATF_TC_BODY(attach_pid1_securelevel,tc)10624e714f4Skamil ATF_TC_BODY(attach_pid1_securelevel, tc)
10724e714f4Skamil {
10824e714f4Skamil int level;
10924e714f4Skamil size_t len = sizeof(level);
11024e714f4Skamil
11124e714f4Skamil ATF_REQUIRE(sysctlbyname("kern.securelevel", &level, &len, NULL, 0)
11224e714f4Skamil != -1);
11324e714f4Skamil
11424e714f4Skamil if (level < 0) {
11524e714f4Skamil atf_tc_skip("Test must be run with securelevel >= 0");
11624e714f4Skamil }
11724e714f4Skamil
11824e714f4Skamil ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
11924e714f4Skamil }
12024e714f4Skamil
12124e714f4Skamil ATF_TC(attach_self);
ATF_TC_HEAD(attach_self,tc)12224e714f4Skamil ATF_TC_HEAD(attach_self, tc)
12324e714f4Skamil {
12424e714f4Skamil atf_tc_set_md_var(tc, "descr",
12524e714f4Skamil "Assert that a debugger cannot attach to self (as it's nonsense)");
12624e714f4Skamil }
12724e714f4Skamil
ATF_TC_BODY(attach_self,tc)12824e714f4Skamil ATF_TC_BODY(attach_self, tc)
12924e714f4Skamil {
13024e714f4Skamil ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1);
13124e714f4Skamil }
13224e714f4Skamil
13324e714f4Skamil ATF_TC(attach_chroot);
ATF_TC_HEAD(attach_chroot,tc)13424e714f4Skamil ATF_TC_HEAD(attach_chroot, tc)
13524e714f4Skamil {
13624e714f4Skamil atf_tc_set_md_var(tc, "descr",
13724e714f4Skamil "Assert that a debugger cannot trace another process unless the "
13824e714f4Skamil "process's root directory is at or below the tracing process's "
13924e714f4Skamil "root");
14024e714f4Skamil
14124e714f4Skamil atf_tc_set_md_var(tc, "require.user", "root");
14224e714f4Skamil }
14324e714f4Skamil
ATF_TC_BODY(attach_chroot,tc)14424e714f4Skamil ATF_TC_BODY(attach_chroot, tc)
14524e714f4Skamil {
14624e714f4Skamil char buf[PATH_MAX];
14724e714f4Skamil pid_t child;
14824e714f4Skamil int fds_toparent[2], fds_fromparent[2];
14924e714f4Skamil int rv;
15024e714f4Skamil uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
15124e714f4Skamil
15224e714f4Skamil (void)memset(buf, '\0', sizeof(buf));
15324e714f4Skamil ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL);
15424e714f4Skamil (void)strlcat(buf, "/dir", sizeof(buf));
15524e714f4Skamil
15624e714f4Skamil ATF_REQUIRE(mkdir(buf, 0500) == 0);
15724e714f4Skamil ATF_REQUIRE(chdir(buf) == 0);
15824e714f4Skamil
15924e714f4Skamil ATF_REQUIRE(pipe(fds_toparent) == 0);
16024e714f4Skamil ATF_REQUIRE(pipe(fds_fromparent) == 0);
16124e714f4Skamil child = atf_utils_fork();
16224e714f4Skamil if (child == 0) {
16324e714f4Skamil FORKEE_ASSERT(close(fds_toparent[0]) == 0);
16424e714f4Skamil FORKEE_ASSERT(close(fds_fromparent[1]) == 0);
16524e714f4Skamil
16624e714f4Skamil FORKEE_ASSERT(chroot(buf) == 0);
16724e714f4Skamil
16824e714f4Skamil rv = write(fds_toparent[1], &msg, sizeof(msg));
16924e714f4Skamil FORKEE_ASSERTX(rv == sizeof(msg));
17024e714f4Skamil
17124e714f4Skamil ATF_REQUIRE_ERRNO(EPERM,
17224e714f4Skamil ptrace(PT_ATTACH, getppid(), NULL, 0) == -1);
17324e714f4Skamil
17424e714f4Skamil rv = read(fds_fromparent[0], &msg, sizeof(msg));
17524e714f4Skamil FORKEE_ASSERTX(rv == sizeof(msg));
17624e714f4Skamil
17724e714f4Skamil _exit(0);
17824e714f4Skamil }
17924e714f4Skamil ATF_REQUIRE(close(fds_toparent[1]) == 0);
18024e714f4Skamil ATF_REQUIRE(close(fds_fromparent[0]) == 0);
18124e714f4Skamil
18224e714f4Skamil printf("Waiting for chrooting of the child PID %d", child);
18324e714f4Skamil rv = read(fds_toparent[0], &msg, sizeof(msg));
18424e714f4Skamil ATF_REQUIRE(rv == sizeof(msg));
18524e714f4Skamil
18624e714f4Skamil printf("Child is ready, it will try to PT_ATTACH to parent\n");
18724e714f4Skamil rv = write(fds_fromparent[1], &msg, sizeof(msg));
18824e714f4Skamil ATF_REQUIRE(rv == sizeof(msg));
18924e714f4Skamil
19024e714f4Skamil printf("fds_fromparent is no longer needed - close it\n");
19124e714f4Skamil ATF_REQUIRE(close(fds_fromparent[1]) == 0);
19224e714f4Skamil
19324e714f4Skamil printf("fds_toparent is no longer needed - close it\n");
19424e714f4Skamil ATF_REQUIRE(close(fds_toparent[0]) == 0);
19524e714f4Skamil }
19624e714f4Skamil
19766a7857dSkamil ATF_TC(traceme_twice);
ATF_TC_HEAD(traceme_twice,tc)19866a7857dSkamil ATF_TC_HEAD(traceme_twice, tc)
19966a7857dSkamil {
20066a7857dSkamil atf_tc_set_md_var(tc, "descr",
20166a7857dSkamil "Assert that a process cannot mark its parent a debugger twice");
20266a7857dSkamil }
20366a7857dSkamil
ATF_TC_BODY(traceme_twice,tc)20466a7857dSkamil ATF_TC_BODY(traceme_twice, tc)
20566a7857dSkamil {
20666a7857dSkamil
20766a7857dSkamil printf("Mark the parent process (PID %d) a debugger of PID %d",
20866a7857dSkamil getppid(), getpid());
20966a7857dSkamil ATF_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) == 0);
21066a7857dSkamil
21166a7857dSkamil printf("Mark the parent process (PID %d) a debugger of PID %d again",
21266a7857dSkamil getppid(), getpid());
21366a7857dSkamil ATF_REQUIRE_ERRNO(EBUSY, ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
21466a7857dSkamil }
21566a7857dSkamil
ATF_TP_ADD_TCS(tp)21624e714f4Skamil ATF_TP_ADD_TCS(tp)
21724e714f4Skamil {
21824e714f4Skamil setvbuf(stdout, NULL, _IONBF, 0);
21924e714f4Skamil setvbuf(stderr, NULL, _IONBF, 0);
22024e714f4Skamil ATF_TP_ADD_TC(tp, attach_pid0);
22124e714f4Skamil ATF_TP_ADD_TC(tp, attach_pid1);
22224e714f4Skamil ATF_TP_ADD_TC(tp, attach_pid1_securelevel);
22324e714f4Skamil ATF_TP_ADD_TC(tp, attach_self);
22424e714f4Skamil ATF_TP_ADD_TC(tp, attach_chroot);
22566a7857dSkamil ATF_TP_ADD_TC(tp, traceme_twice);
22624e714f4Skamil
22724e714f4Skamil return atf_no_error();
22824e714f4Skamil }
229