summaryrefslogtreecommitdiff
path: root/src/disable_ptrace_request.c
blob: 0608e54fe4657b09877caf6ba893e648f7b9d8f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * A helper that executes the specified program
 * with the ptrace request disabled.
 *
 * Copyright (c) 2015-2021 The strace developers.
 * All rights reserved.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "defs.h"
#include "ptrace.h"
#include "scno.h"
#include <signal.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <linux/filter.h>
#include <linux/seccomp.h>

#ifndef HAVE_PROGRAM_INVOCATION_NAME
char *program_invocation_name;
#endif

void ATTRIBUTE_NORETURN
die(void)
{
	exit(1);
}

static void
init(int argc, char **argv)
{
	if (!program_invocation_name || !*program_invocation_name) {
		static char name[] = DEFAULT_PROGRAM_INVOCATION_NAME;
		program_invocation_name =
			(argc > 0 && argv[0] && *argv[0]) ? argv[0] : name;
	}
}

#if defined DISABLE_PTRACE_REQUEST \
 && defined PR_SET_NO_NEW_PRIVS \
 && defined PR_SET_SECCOMP \
 && defined BPF_JUMP \
 && defined BPF_STMT \
 && defined HAVE_FORK

static unsigned int
get_arch(void)
{
	pid_t pid = fork();
	if (pid < 0)
		perror_msg_and_die("fork");

	if (pid == 0) {
		/* get the pid before PTRACE_TRACEME */
		pid = getpid();
		if (ptrace(PTRACE_TRACEME, 0, 0, 0))
			perror_msg_and_die("PTRACE_TRACEME");
		kill(pid, SIGSTOP);
		/* unreachable */
		_exit(1);
	}

	int status = 0;
	if (waitpid(pid, &status, 0) != pid ||
	    !WIFSTOPPED(status) ||
	    WSTOPSIG(status) != SIGSTOP) {
		/* cannot happen */
		perror_msg_and_die("waitpid: status = %d", status);
	}

	static const unsigned int size =
		offsetof(struct_ptrace_syscall_info, entry);
	struct_ptrace_syscall_info psi = { .arch = 0 };

	long rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, &psi);

	int saved_errno = errno;
	(void) kill(pid, SIGKILL);
	(void) waitpid(pid, NULL, 0);
	errno = saved_errno;

	/*
	 * Skip if PTRACE_GET_SYSCALL_INFO is not available
	 * or behaves in an unexpected way.
	 */
	if (rc < (long) size ||
	    psi.op != PTRACE_SYSCALL_INFO_NONE ||
	    psi.arch == 0) {
		perror_msg_and_die("PTRACE_GET_SYSCALL_INFO");
	}

	return psi.arch;
}

int
main(int argc, char **argv)
{
	init(argc, argv);

	if (argc < 2)
		error_msg_and_die("Insufficient arguments");

	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
		perror_msg_and_die("PR_SET_NO_NEW_PRIVS");

	struct sock_filter filter[] = {
		/* load the architecture */
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
			 offsetof(struct seccomp_data, arch)),
		/* jump to "allow" if the architecture does not match */
		BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, get_arch(), 0, 5),
		/* load the syscall number */
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
			 offsetof(struct seccomp_data, nr)),
		/* jump to "allow" if it is not equal to __NR_ptrace */
		BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, __NR_ptrace, 0, 3),
		/* load the 1st syscall argument */
		BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
			 offsetof(struct seccomp_data, args[0])
			 + (is_bigendian ? sizeof(uint32_t) : 0)),
		/* jump to "allow" if it is not equal to DISABLE_PTRACE_REQUEST */
		BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, DISABLE_PTRACE_REQUEST, 0, 1),
		/* reject */
		BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EIO),
		/* allow */
		BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
	};

	const struct sock_fprog prog = {
		.len = ARRAY_SIZE(filter),
		.filter = filter,
	};

	if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
		perror_msg_and_die("PR_SET_SECCOMP");

	(void) execvp(argv[1], argv + 1);
	perror_msg_and_die("execvp: %s", argv[1]);
}

#else

int
main(int argc, char **argv)
{
	init(argc, argv);
	error_msg_and_die("Operation not supported");
}

#endif