11.7Sriastrad/*	$NetBSD: t_ptrace.c,v 1.7 2025/05/02 02:24:44 riastradh Exp $	*/
21.1Skamil
31.1Skamil/*-
41.1Skamil * Copyright (c) 2016 The NetBSD Foundation, Inc.
51.1Skamil * All rights reserved.
61.1Skamil *
71.1Skamil * Redistribution and use in source and binary forms, with or without
81.1Skamil * modification, are permitted provided that the following conditions
91.1Skamil * are met:
101.1Skamil * 1. Redistributions of source code must retain the above copyright
111.1Skamil *    notice, this list of conditions and the following disclaimer.
121.1Skamil * 2. Redistributions in binary form must reproduce the above copyright
131.1Skamil *    notice, this list of conditions and the following disclaimer in the
141.1Skamil *    documentation and/or other materials provided with the distribution.
151.1Skamil *
161.1Skamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
171.1Skamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
181.1Skamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
191.1Skamil * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
201.1Skamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
211.1Skamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
221.1Skamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
231.1Skamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
241.1Skamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
251.1Skamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
261.1Skamil * POSSIBILITY OF SUCH DAMAGE.
271.1Skamil */
281.1Skamil
291.1Skamil#include <sys/cdefs.h>
301.7Sriastrad__RCSID("$NetBSD: t_ptrace.c,v 1.7 2025/05/02 02:24:44 riastradh Exp $");
311.1Skamil
321.1Skamil#include <sys/param.h>
331.1Skamil#include <sys/types.h>
341.1Skamil#include <sys/ptrace.h>
351.1Skamil#include <sys/stat.h>
361.1Skamil#include <sys/sysctl.h>
371.1Skamil#include <err.h>
381.1Skamil#include <errno.h>
391.1Skamil#include <unistd.h>
401.1Skamil
411.1Skamil#include <atf-c.h>
421.1Skamil
431.1Skamil#include "h_macros.h"
441.1Skamil
451.1Skamil/*
461.1Skamil * A child process cannot call atf functions and expect them to magically
471.1Skamil * work like in the parent.
481.1Skamil * The printf(3) messaging from a child will not work out of the box as well
491.5Sandvar * without establishing a communication protocol with its parent. To not
501.1Skamil * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
511.1Skamil * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
521.1Skamil */
531.7Sriastrad#define FORKEE_ASSERTX(x)						      \
541.7Sriastraddo {									      \
551.7Sriastrad	int ret = (x);							      \
561.7Sriastrad	if (!ret)							      \
571.7Sriastrad		errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",    \
581.7Sriastrad		    __FILE__, __LINE__, __func__, #x);			      \
591.1Skamil} while (0)
601.1Skamil
611.7Sriastrad#define FORKEE_ASSERT(x)						      \
621.7Sriastraddo {									      \
631.7Sriastrad	int ret = (x);							      \
641.7Sriastrad	if (!ret)							      \
651.7Sriastrad		err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",     \
661.7Sriastrad		    __FILE__, __LINE__, __func__, #x);			      \
671.7Sriastrad} while (0)
681.7Sriastrad
691.7Sriastrad#define FORKEE_ASSERT_EQ(x, y)						      \
701.7Sriastraddo {									      \
711.7Sriastrad	uintmax_t vx = (x);						      \
721.7Sriastrad	uintmax_t vy = (y);						      \
731.7Sriastrad	int ret = vx == vy;						      \
741.7Sriastrad	if (!ret)							      \
751.7Sriastrad		errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: "	      \
761.7Sriastrad		    "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__,	      \
771.7Sriastrad		    #x, vx, #y, vy);					      \
781.1Skamil} while (0)
791.1Skamil
801.1SkamilATF_TC(attach_pid0);
811.1SkamilATF_TC_HEAD(attach_pid0, tc)
821.1Skamil{
831.1Skamil	atf_tc_set_md_var(tc, "descr",
841.1Skamil	    "Assert that a debugger cannot attach to PID 0");
851.1Skamil}
861.1Skamil
871.1SkamilATF_TC_BODY(attach_pid0, tc)
881.1Skamil{
891.1Skamil	errno = 0;
901.1Skamil	ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1);
911.1Skamil}
921.1Skamil
931.1SkamilATF_TC(attach_pid1);
941.1SkamilATF_TC_HEAD(attach_pid1, tc)
951.1Skamil{
961.1Skamil	atf_tc_set_md_var(tc, "descr",
971.1Skamil	    "Assert that a debugger cannot attach to PID 1 (as non-root)");
981.1Skamil
991.1Skamil	atf_tc_set_md_var(tc, "require.user", "unprivileged");
1001.1Skamil}
1011.1Skamil
1021.1SkamilATF_TC_BODY(attach_pid1, tc)
1031.1Skamil{
1041.1Skamil	ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
1051.1Skamil}
1061.1Skamil
1071.1SkamilATF_TC(attach_pid1_securelevel);
1081.1SkamilATF_TC_HEAD(attach_pid1_securelevel, tc)
1091.1Skamil{
1101.1Skamil	atf_tc_set_md_var(tc, "descr",
1111.1Skamil	    "Assert that a debugger cannot attach to PID 1 with "
1121.1Skamil	    "securelevel >= 0 (as root)");
1131.1Skamil
1141.1Skamil	atf_tc_set_md_var(tc, "require.user", "root");
1151.1Skamil}
1161.1Skamil
1171.1SkamilATF_TC_BODY(attach_pid1_securelevel, tc)
1181.1Skamil{
1191.1Skamil	int level;
1201.1Skamil	size_t len = sizeof(level);
1211.1Skamil
1221.7Sriastrad	RL(sysctlbyname("kern.securelevel", &level, &len, NULL, 0));
1231.1Skamil
1241.1Skamil	if (level < 0) {
1251.1Skamil		atf_tc_skip("Test must be run with securelevel >= 0");
1261.1Skamil	}
1271.1Skamil
1281.1Skamil	ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
1291.1Skamil}
1301.1Skamil
1311.1SkamilATF_TC(attach_self);
1321.1SkamilATF_TC_HEAD(attach_self, tc)
1331.1Skamil{
1341.1Skamil	atf_tc_set_md_var(tc, "descr",
1351.1Skamil	    "Assert that a debugger cannot attach to self (as it's nonsense)");
1361.1Skamil}
1371.1Skamil
1381.1SkamilATF_TC_BODY(attach_self, tc)
1391.1Skamil{
1401.1Skamil	ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1);
1411.1Skamil}
1421.1Skamil
1431.1SkamilATF_TC(attach_chroot);
1441.1SkamilATF_TC_HEAD(attach_chroot, tc)
1451.1Skamil{
1461.1Skamil	atf_tc_set_md_var(tc, "descr",
1471.1Skamil	    "Assert that a debugger cannot trace another process unless the "
1481.1Skamil	    "process's root directory is at or below the tracing process's "
1491.1Skamil	    "root");
1501.1Skamil
1511.1Skamil	atf_tc_set_md_var(tc, "require.user", "root");
1521.6Sriastrad}
1531.1Skamil
1541.1SkamilATF_TC_BODY(attach_chroot, tc)
1551.1Skamil{
1561.1Skamil	char buf[PATH_MAX];
1571.1Skamil	pid_t child;
1581.1Skamil	int fds_toparent[2], fds_fromparent[2];
1591.1Skamil	int rv;
1601.1Skamil	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
1611.1Skamil
1621.1Skamil	(void)memset(buf, '\0', sizeof(buf));
1631.7Sriastrad	REQUIRE_LIBC(getcwd(buf, sizeof(buf)), NULL);
1641.1Skamil	(void)strlcat(buf, "/dir", sizeof(buf));
1651.1Skamil
1661.7Sriastrad	RL(mkdir(buf, 0500));
1671.7Sriastrad	RL(chdir(buf));
1681.1Skamil
1691.7Sriastrad	RL(pipe(fds_toparent));
1701.7Sriastrad	RL(pipe(fds_fromparent));
1711.1Skamil	child = atf_utils_fork();
1721.1Skamil	if (child == 0) {
1731.1Skamil		FORKEE_ASSERT(close(fds_toparent[0]) == 0);
1741.1Skamil		FORKEE_ASSERT(close(fds_fromparent[1]) == 0);
1751.1Skamil
1761.1Skamil		FORKEE_ASSERT(chroot(buf) == 0);
1771.1Skamil
1781.7Sriastrad		FORKEE_ASSERT((rv = write(fds_toparent[1], &msg, sizeof(msg)))
1791.7Sriastrad		    != -1);
1801.7Sriastrad		FORKEE_ASSERT_EQ(rv, sizeof(msg));
1811.7Sriastrad
1821.7Sriastrad		if (ptrace(PT_ATTACH, getppid(), NULL, 0) == 0) {
1831.7Sriastrad			errx(EXIT_FAILURE, "%s unexpectedly succeeded",
1841.7Sriastrad			    "ptrace(PT_ATTACH, getppid(), NULL, 0)");
1851.7Sriastrad		} else if (errno != EPERM) {
1861.7Sriastrad			err(EXIT_FAILURE, "%s failed but not with EPERM",
1871.7Sriastrad			    "ptrace(PT_ATTACH, getppid(), NULL, 0)");
1881.7Sriastrad		}
1891.7Sriastrad
1901.7Sriastrad		FORKEE_ASSERT((rv = read(fds_fromparent[0], &msg, sizeof(msg)))
1911.7Sriastrad		    != -1);
1921.7Sriastrad		FORKEE_ASSERT_EQ(rv, sizeof(msg));
1931.1Skamil
1941.1Skamil		_exit(0);
1951.1Skamil	}
1961.7Sriastrad	RL(close(fds_toparent[1]));
1971.7Sriastrad	RL(close(fds_fromparent[0]));
1981.1Skamil
1991.1Skamil	printf("Waiting for chrooting of the child PID %d", child);
2001.7Sriastrad	RL(rv = read(fds_toparent[0], &msg, sizeof(msg)));
2011.7Sriastrad	ATF_REQUIRE(rv == sizeof(msg));
2021.1Skamil
2031.1Skamil	printf("Child is ready, it will try to PT_ATTACH to parent\n");
2041.7Sriastrad	RL(rv = write(fds_fromparent[1], &msg, sizeof(msg)));
2051.1Skamil	ATF_REQUIRE(rv == sizeof(msg));
2061.1Skamil
2071.1Skamil        printf("fds_fromparent is no longer needed - close it\n");
2081.7Sriastrad        RL(close(fds_fromparent[1]));
2091.1Skamil
2101.1Skamil        printf("fds_toparent is no longer needed - close it\n");
2111.7Sriastrad        RL(close(fds_toparent[0]));
2121.1Skamil}
2131.1Skamil
2141.2SkamilATF_TC(traceme_twice);
2151.2SkamilATF_TC_HEAD(traceme_twice, tc)
2161.2Skamil{
2171.2Skamil	atf_tc_set_md_var(tc, "descr",
2181.2Skamil	    "Assert that a process cannot mark its parent a debugger twice");
2191.2Skamil}
2201.2Skamil
2211.2SkamilATF_TC_BODY(traceme_twice, tc)
2221.2Skamil{
2231.2Skamil
2241.2Skamil	printf("Mark the parent process (PID %d) a debugger of PID %d",
2251.2Skamil	       getppid(), getpid());
2261.7Sriastrad	RL(ptrace(PT_TRACE_ME, 0, NULL, 0));
2271.2Skamil
2281.2Skamil	printf("Mark the parent process (PID %d) a debugger of PID %d again",
2291.2Skamil	       getppid(), getpid());
2301.2Skamil	ATF_REQUIRE_ERRNO(EBUSY, ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
2311.2Skamil}
2321.2Skamil
2331.1SkamilATF_TP_ADD_TCS(tp)
2341.1Skamil{
2351.1Skamil	setvbuf(stdout, NULL, _IONBF, 0);
2361.1Skamil	setvbuf(stderr, NULL, _IONBF, 0);
2371.1Skamil	ATF_TP_ADD_TC(tp, attach_pid0);
2381.1Skamil	ATF_TP_ADD_TC(tp, attach_pid1);
2391.1Skamil	ATF_TP_ADD_TC(tp, attach_pid1_securelevel);
2401.1Skamil	ATF_TP_ADD_TC(tp, attach_self);
2411.1Skamil	ATF_TP_ADD_TC(tp, attach_chroot);
2421.2Skamil	ATF_TP_ADD_TC(tp, traceme_twice);
2431.1Skamil
2441.1Skamil	return atf_no_error();
2451.1Skamil}
246