1 /*
2 * A helper that executes the specified program
3 * with the ptrace request disabled.
4 *
5 * Copyright (c) 2015-2021 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "defs.h"
12 #include "ptrace.h"
13 #include "scno.h"
14 #include <signal.h>
15 #include <sys/prctl.h>
16 #include <sys/wait.h>
17 #include <linux/filter.h>
18 #include <linux/seccomp.h>
19
20 #ifndef HAVE_PROGRAM_INVOCATION_NAME
21 char *program_invocation_name;
22 #endif
23
24 void ATTRIBUTE_NORETURN
25 die(void)
26 {
27 exit(1);
28 }
29
30 static void
31 init(int argc, char **argv)
32 {
33 if (!program_invocation_name || !*program_invocation_name) {
34 static char name[] = DEFAULT_PROGRAM_INVOCATION_NAME;
35 program_invocation_name =
36 (argc > 0 && argv[0] && *argv[0]) ? argv[0] : name;
37 }
38 }
39
40 #if defined DISABLE_PTRACE_REQUEST \
41 && defined PR_SET_NO_NEW_PRIVS \
42 && defined PR_SET_SECCOMP \
43 && defined BPF_JUMP \
44 && defined BPF_STMT \
45 && defined HAVE_FORK
46
47 static unsigned int
48 get_arch(void)
49 {
50 pid_t pid = fork();
51 if (pid < 0)
52 perror_msg_and_die("fork");
53
54 if (pid == 0) {
55 /* get the pid before PTRACE_TRACEME */
56 pid = getpid();
57 if (ptrace(PTRACE_TRACEME, 0, 0, 0))
58 perror_msg_and_die("PTRACE_TRACEME");
59 kill(pid, SIGSTOP);
60 /* unreachable */
61 _exit(1);
62 }
63
64 int status = 0;
65 if (waitpid(pid, &status, 0) != pid ||
66 !WIFSTOPPED(status) ||
67 WSTOPSIG(status) != SIGSTOP) {
68 /* cannot happen */
69 perror_msg_and_die("waitpid: status = %d", status);
70 }
71
72 static const unsigned int size =
73 offsetof(struct_ptrace_syscall_info, entry);
74 struct_ptrace_syscall_info psi = { .arch = 0 };
75
76 long rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, &psi);
77
78 int saved_errno = errno;
79 (void) kill(pid, SIGKILL);
80 (void) waitpid(pid, NULL, 0);
81 errno = saved_errno;
82
83 /*
84 * Skip if PTRACE_GET_SYSCALL_INFO is not available
85 * or behaves in an unexpected way.
86 */
87 if (rc < (long) size ||
88 psi.op != PTRACE_SYSCALL_INFO_NONE ||
89 psi.arch == 0) {
90 perror_msg_and_die("PTRACE_GET_SYSCALL_INFO");
91 }
92
93 return psi.arch;
94 }
95
96 int
97 main(int argc, char **argv)
98 {
99 init(argc, argv);
100
101 if (argc < 2)
102 error_msg_and_die("Insufficient arguments");
103
104 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
105 perror_msg_and_die("PR_SET_NO_NEW_PRIVS");
106
107 struct sock_filter filter[] = {
108 /* load the architecture */
109 BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
110 offsetof(struct seccomp_data, arch)),
111 /* jump to "allow" if the architecture does not match */
112 BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, get_arch(), 0, 5),
113 /* load the syscall number */
114 BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
115 offsetof(struct seccomp_data, nr)),
116 /* jump to "allow" if it is not equal to __NR_ptrace */
117 BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, __NR_ptrace, 0, 3),
118 /* load the 1st syscall argument */
119 BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
120 offsetof(struct seccomp_data, args[0])
121 + (is_bigendian ? sizeof(uint32_t) : 0)),
122 /* jump to "allow" if it is not equal to DISABLE_PTRACE_REQUEST */
123 BPF_JUMP(BPF_JMP | BPF_K | BPF_JEQ, DISABLE_PTRACE_REQUEST, 0, 1),
124 /* reject */
125 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EIO),
126 /* allow */
127 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
128 };
129
130 const struct sock_fprog prog = {
131 .len = ARRAY_SIZE(filter),
132 .filter = filter,
133 };
134
135 if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog))
136 perror_msg_and_die("PR_SET_SECCOMP");
137
138 (void) execvp(argv[1], argv + 1);
139 perror_msg_and_die("execvp: %s", argv[1]);
140 }
141
142 #else
143
144 int
145 main(int argc, char **argv)
146 {
147 init(argc, argv);
148 error_msg_and_die("Operation not supported");
149 }
150
151 #endif