git: 44952722a00b - stable/14 - tests: Add ktrace capability violation test cases

From: Jake Freeland <jfree_at_FreeBSD.org>
Date: Sun, 12 May 2024 00:08:32 UTC
The branch stable/14 has been updated by jfree:

URL: https://cgit.FreeBSD.org/src/commit/?id=44952722a00b92ae42752428f0d6a673b1af8f82

commit 44952722a00b92ae42752428f0d6a673b1af8f82
Author:     Jake Freeland <jfree@FreeBSD.org>
AuthorDate: 2024-04-06 18:31:30 +0000
Commit:     Jake Freeland <jfree@FreeBSD.org>
CommitDate: 2024-05-11 23:57:44 +0000

    tests: Add ktrace capability violation test cases
    
    Introduce regression tests for ktrace(2) that target capability
    violations.
    
    These test cases ensure that ktrace(2) records these violations:
    - CAPFAIL_NOTCAPABLE
    - CAPFAIL_INCREASE
    - CAPFAIL_SYSCALL
    - CAPFAIL_SIGNAL
    - CAPFAIL_PROTO
    - CAPFAIL_SOCKADDR
    - CAPFAIL_NAMEI
    - CAPFAIL_CPUSET
    
    A portion of these test cases create processes that do NOT enter
    capability mode, but raise violations. This is intended behavior.
    Users may run `ktrace -t p` on non-Capsicumized programs to detect
    violations that would occur if the process were in capability mode.
    
    Reviewed by:    markj
    Approved by:    markj (mentor)
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D40682
    
    (cherry picked from commit 2f39a986641ecebaa9080ca28118903bd9707a1f)
---
 tests/sys/kern/Makefile      |   2 +
 tests/sys/kern/ktrace_test.c | 488 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 490 insertions(+)

diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index b95be12d9665..d78c761feaac 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -21,6 +21,7 @@ ATF_TESTS_C+=	kill_zombie
 .if ${MK_OPENSSL} != "no"
 ATF_TESTS_C+=	ktls_test
 .endif
+ATF_TESTS_C+=	ktrace_test
 ATF_TESTS_C+=	module_test
 ATF_TESTS_C+=	ptrace_test
 TEST_METADATA.ptrace_test+=		timeout="15"
@@ -79,6 +80,7 @@ LIBADD.socket_msg_waitall+=		pthread
 LIBADD.sendfile_helper+=		pthread
 LIBADD.fdgrowtable_test+=		util pthread kvm procstat
 LIBADD.sigwait+=			rt
+LIBADD.ktrace_test+=			sysdecode
 
 NETBSD_ATF_TESTS_C+=	lockf_test
 NETBSD_ATF_TESTS_C+=	mqueue_test
diff --git a/tests/sys/kern/ktrace_test.c b/tests/sys/kern/ktrace_test.c
new file mode 100644
index 000000000000..49e2ed05fed9
--- /dev/null
+++ b/tests/sys/kern/ktrace_test.c
@@ -0,0 +1,488 @@
+/*-
+ * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Jake Freeland <jfree@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/capsicum.h>
+#include <sys/cpuset.h>
+#include <sys/ktrace.h>
+#include <sys/socket.h>
+#include <sys/sysent.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <machine/sysarch.h>
+#include <netinet/in.h>
+
+#include <atf-c.h>
+#include <capsicum_helpers.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <signal.h>
+#include <sysdecode.h>
+
+/*
+ * A variant of ATF_REQUIRE that is suitable for use in child
+ * processes.  This only works if the parent process is tripped up by
+ * the early exit and fails some requirement itself.
+ */
+#define	CHILD_REQUIRE(exp) do {				\
+	if (!(exp))					\
+		child_fail_require(__FILE__, __LINE__,	\
+		    #exp " not met\n");			\
+} while (0)
+#define	CHILD_REQUIRE_EQ(actual, expected) do {			\
+	__typeof__(expected) _e = expected;			\
+	__typeof__(actual) _a = actual;				\
+	if (_e != _a)						\
+		child_fail_require(__FILE__, __LINE__, #actual	\
+		    " (%jd) == " #expected " (%jd) not met\n",	\
+		    (intmax_t)_a, (intmax_t)_e);		\
+} while (0)
+
+static __dead2 void
+child_fail_require(const char *file, int line, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[1024];
+
+	/* Use write() not fprintf() to avoid possible duplicate output. */
+	snprintf(buf, sizeof(buf), "%s:%d: ", file, line);
+	write(STDERR_FILENO, buf, strlen(buf));
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	write(STDERR_FILENO, buf, strlen(buf));
+	va_end(ap);
+
+	_exit(32);
+}
+
+/*
+ * Determine sysdecode ABI based on proc's ABI in sv_flags.
+ */
+static enum sysdecode_abi
+syscallabi(u_int sv_flags)
+{
+	switch (sv_flags & SV_ABI_MASK) {
+	case SV_ABI_FREEBSD:
+		return (SYSDECODE_ABI_FREEBSD);
+	case SV_ABI_LINUX:
+#ifdef __LP64__
+		if ((sv_flags & SV_ILP32) != 0)
+			return (SYSDECODE_ABI_LINUX32);
+#endif
+		return (SYSDECODE_ABI_LINUX);
+	}
+	return (SYSDECODE_ABI_UNKNOWN);
+}
+
+/*
+ * Start tracing capability violations and notify child that it can execute.
+ * Return @numv capability violations from child in @v.
+ */
+static void
+cap_trace_child(int cpid, struct ktr_cap_fail *v, int numv)
+{
+	struct ktr_header header;
+	int error, fd, i;
+
+	ATF_REQUIRE((fd = open("ktrace.out",
+	    O_RDONLY | O_CREAT | O_TRUNC)) != -1);
+	ATF_REQUIRE(ktrace("ktrace.out", KTROP_SET,
+	    KTRFAC_CAPFAIL, cpid) != -1);
+	/* Notify child that we've starting tracing. */
+	ATF_REQUIRE(kill(cpid, SIGUSR1) != -1);
+	/* Wait for child to raise violation and exit. */
+	ATF_REQUIRE(waitpid(cpid, &error, 0) != -1);
+	ATF_REQUIRE(WIFEXITED(error));
+	ATF_REQUIRE_EQ(WEXITSTATUS(error), 0);
+	/* Read ktrace header and ensure violation occurred. */
+	for (i = 0; i < numv; ++i) {
+		ATF_REQUIRE((error = read(fd, &header, sizeof(header))) != -1);
+		ATF_REQUIRE_EQ(error, sizeof(header));
+		ATF_REQUIRE_EQ(header.ktr_len, sizeof(*v));
+		ATF_REQUIRE_EQ(header.ktr_pid, cpid);
+		/* Read the capability violation. */
+		ATF_REQUIRE((error = read(fd, v + i,
+		    sizeof(*v))) != -1);
+		ATF_REQUIRE_EQ(error, sizeof(*v));
+	}
+	ATF_REQUIRE(close(fd) != -1);
+}
+
+/*
+ * Test if ktrace will record an operation that is done with
+ * insufficient rights.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_not_capable);
+ATF_TC_BODY(ktrace__cap_not_capable, tc)
+{
+	struct ktr_cap_fail violation;
+	cap_rights_t rights;
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Limit fd rights to CAP_READ. */
+		cap_rights_init(&rights, CAP_READ);
+		CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
+		CHILD_REQUIRE(caph_enter() != -1);
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/* Write without CAP_WRITE. */
+		CHILD_REQUIRE(write(STDIN_FILENO, &pid, sizeof(pid)) == -1);
+		CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_NOTCAPABLE);
+	ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
+	    CAP_WRITE));
+}
+
+/*
+ * Test if ktrace will record an attempt to increase rights.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_increase_rights);
+ATF_TC_BODY(ktrace__cap_increase_rights, tc)
+{
+	struct ktr_cap_fail violation;
+	cap_rights_t rights;
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Limit fd rights to CAP_READ. */
+		cap_rights_init(&rights, CAP_READ);
+		CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) != -1);
+		CHILD_REQUIRE(caph_enter() != -1);
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/* Increase fd rights to include CAP_WRITE. */
+		cap_rights_set(&rights, CAP_WRITE);
+		CHILD_REQUIRE(caph_rights_limit(STDIN_FILENO, &rights) == -1);
+		CHILD_REQUIRE_EQ(errno, ENOTCAPABLE);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_INCREASE);
+	ATF_REQUIRE(cap_rights_is_set(&violation.cap_data.cap_needed,
+	    CAP_WRITE));
+}
+
+/*
+ * Test if disallowed syscalls are reported as capability violations.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_syscall);
+ATF_TC_BODY(ktrace__cap_syscall, tc)
+{
+	struct kinfo_file kinf;
+	struct ktr_cap_fail violation[2];
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/* chdir() is not permitted in capability mode. */
+		CHILD_REQUIRE(chdir(".") != -1);
+		kinf.kf_structsize = sizeof(struct kinfo_file);
+		/*
+		 * fcntl() is permitted in capability mode,
+		 * but the F_KINFO cmd is not.
+		 */
+		CHILD_REQUIRE(fcntl(STDIN_FILENO, F_KINFO, &kinf) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, violation, 2);
+	ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_SYSCALL);
+	error = syscallabi(violation[0].cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
+	    "chdir");
+
+	ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_SYSCALL);
+	error = syscallabi(violation[1].cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
+	    "fcntl");
+	ATF_REQUIRE_EQ(violation[1].cap_data.cap_int, F_KINFO);
+}
+
+/*
+ * Test if sending a signal to another process is reported as
+ * a signal violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_signal);
+ATF_TC_BODY(ktrace__cap_signal, tc)
+{
+	struct ktr_cap_fail violation;
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/*
+		 * Signals may only be sent to ourself. Sending signals
+		 * to other processes is not allowed in capability mode.
+		 */
+		CHILD_REQUIRE(kill(getppid(), SIGCONT) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SIGNAL);
+	error = syscallabi(violation.cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+	    "kill");
+	ATF_REQUIRE_EQ(violation.cap_data.cap_int, SIGCONT);
+}
+
+/*
+ * Test if opening a socket with a restricted protocol is reported
+ * as a protocol violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_proto);
+ATF_TC_BODY(ktrace__cap_proto, tc)
+{
+	struct ktr_cap_fail violation;
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/*
+		 * Certain protocols may not be used in capability mode.
+		 * ICMP's raw-protocol interface is not allowed.
+		 */
+		CHILD_REQUIRE(close(socket(AF_INET, SOCK_RAW,
+		    IPPROTO_ICMP)) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_PROTO);
+	error = syscallabi(violation.cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+	    "socket");
+	ATF_REQUIRE_EQ(violation.cap_data.cap_int, IPPROTO_ICMP);
+}
+
+/*
+ * Test if sending data to an address using a socket is
+ * reported as a sockaddr violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_sockaddr);
+ATF_TC_BODY(ktrace__cap_sockaddr, tc)
+{
+	struct sockaddr_in addr = { }, *saddr;
+	struct ktr_cap_fail violation;
+	sigset_t set = { };
+	pid_t pid;
+	int error, sfd;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	CHILD_REQUIRE((sfd = socket(AF_INET, SOCK_DGRAM,
+	    IPPROTO_UDP)) != -1);
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(5000);
+	addr.sin_addr.s_addr = INADDR_ANY;
+	CHILD_REQUIRE(bind(sfd, (const struct sockaddr *)&addr,
+	    sizeof(addr)) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/*
+		 * Sending data to an address is not permitted.
+		 * In this case, sending data to @addr causes a
+		 * violation.
+		 */
+		CHILD_REQUIRE(sendto(sfd, NULL, 0, 0,
+		    (const struct sockaddr *)&addr, sizeof(addr)) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_SOCKADDR);
+	error = syscallabi(violation.cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+	    "sendto");
+	saddr = (struct sockaddr_in *)&violation.cap_data.cap_sockaddr;
+	ATF_REQUIRE_EQ(saddr->sin_family, AF_INET);
+	ATF_REQUIRE_EQ(saddr->sin_port, htons(5000));
+	ATF_REQUIRE_EQ(saddr->sin_addr.s_addr, INADDR_ANY);
+	close(sfd);
+}
+
+/*
+ * Test if openat() with AT_FDCWD and absolute path are reported
+ * as namei violations.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_namei);
+ATF_TC_BODY(ktrace__cap_namei, tc)
+{
+	struct ktr_cap_fail violation[2];
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/*
+		 * The AT_FDCWD file descriptor has not been opened
+		 * and will be inaccessible in capability mode.
+		 */
+		CHILD_REQUIRE(close(openat(AT_FDCWD, "ktrace.out",
+		    O_RDONLY | O_CREAT)) != -1);
+		/*
+		 * Absolute paths are inaccessible in capability mode.
+		 */
+		CHILD_REQUIRE(close(openat(-1, "/", O_RDONLY)) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, violation, 2);
+	ATF_REQUIRE_EQ(violation[0].cap_type, CAPFAIL_NAMEI);
+	error = syscallabi(violation[0].cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[0].cap_code),
+	    "openat");
+	ATF_REQUIRE_STREQ(violation[0].cap_data.cap_path, "AT_FDCWD");
+
+	ATF_REQUIRE_EQ(violation[1].cap_type, CAPFAIL_NAMEI);
+	error = syscallabi(violation[1].cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation[1].cap_code),
+	    "openat");
+	ATF_REQUIRE_STREQ(violation[1].cap_data.cap_path, "/");
+}
+
+/*
+ * Test if changing another process's cpu set is recorded as
+ * a cpuset violation.
+ */
+ATF_TC_WITHOUT_HEAD(ktrace__cap_cpuset);
+ATF_TC_BODY(ktrace__cap_cpuset, tc)
+{
+	struct ktr_cap_fail violation;
+	cpuset_t cpuset_mask = { };
+	sigset_t set = { };
+	pid_t pid;
+	int error;
+
+	/* Block SIGUSR1 so child does not terminate. */
+	ATF_REQUIRE(sigaddset(&set, SIGUSR1) != -1);
+	ATF_REQUIRE(sigprocmask(SIG_BLOCK, &set, NULL) != -1);
+
+	ATF_REQUIRE((pid = fork()) != -1);
+	if (pid == 0) {
+		/* Wait until ktrace has started. */
+		CHILD_REQUIRE(sigwait(&set, &error) != -1);
+		CHILD_REQUIRE_EQ(error, SIGUSR1);
+		/*
+		 * Set cpu 0 affinity for parent process.
+		 * Other process's cpu sets are restricted in capability
+		 * mode, so this will raise a violation.
+		 */
+		CPU_SET(0, &cpuset_mask);
+		CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID,
+		    getppid(), sizeof(cpuset_mask), &cpuset_mask) != -1);
+		exit(0);
+	}
+
+	cap_trace_child(pid, &violation, 1);
+	ATF_REQUIRE_EQ(violation.cap_type, CAPFAIL_CPUSET);
+	error = syscallabi(violation.cap_svflags);
+	ATF_REQUIRE_STREQ(sysdecode_syscallname(error, violation.cap_code),
+	    "cpuset_setaffinity");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, ktrace__cap_not_capable);
+	ATF_TP_ADD_TC(tp, ktrace__cap_increase_rights);
+	ATF_TP_ADD_TC(tp, ktrace__cap_syscall);
+	ATF_TP_ADD_TC(tp, ktrace__cap_signal);
+	ATF_TP_ADD_TC(tp, ktrace__cap_proto);
+	ATF_TP_ADD_TC(tp, ktrace__cap_sockaddr);
+	ATF_TP_ADD_TC(tp, ktrace__cap_namei);
+	ATF_TP_ADD_TC(tp, ktrace__cap_cpuset);
+	return (atf_no_error());
+}