1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
4 * Author: Yang Xu <[email protected]>
5 */
6
7 /*\
8 * [Description]
9 *
10 * Test PR_GET_SECCOMP and PR_SET_SECCOMP of prctl(2).
11 *
12 * - If PR_SET_SECCOMP sets the SECCOMP_MODE_STRICT for the calling thread,
13 * the only system call that the thread is permitted to make are read(2),
14 * write(2),_exit(2)(but not exit_group(2)), and sigreturn(2). Other
15 * system calls result in the delivery of a SIGKILL signal. This operation
16 * is available only if the kernel is configured with CONFIG_SECCOMP enabled.
17 *
18 * - If PR_SET_SECCOMP sets the SECCOMP_MODE_FILTER for the calling thread,
19 * the system calls allowed are defined by a pointer to a Berkeley Packet
20 * Filter. Other system calls result int the delivery of a SIGSYS signal
21 * with SECCOMP_RET_KILL. The SECCOMP_SET_MODE_FILTER operation is available
22 * only if the kernel is configured with CONFIG_SECCOMP_FILTER enabled.
23 *
24 * - If SECCOMP_MODE_FILTER filters permit fork(2), then the seccomp mode
25 * is inherited by children created by fork(2).
26 */
27
28 #include <errno.h>
29 #include <signal.h>
30 #include <sys/prctl.h>
31 #include <sys/wait.h>
32 #include <sys/types.h>
33 #include <linux/filter.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <stddef.h>
37 #include "tst_test.h"
38 #include "lapi/syscalls.h"
39 #include "lapi/prctl.h"
40 #include "config.h"
41 #include "lapi/seccomp.h"
42
43 #define FNAME "filename"
44
45 static const struct sock_filter strict_filter[] = {
46 BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
47
48 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_waitid, 7, 0),
49 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_rt_sigprocmask, 6, 0),
50 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_close, 5, 0),
51 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_exit, 4, 0),
52 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_wait4, 3, 0),
53 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_write, 2, 0),
54 BPF_JUMP(BPF_JMP | BPF_JEQ, __NR_clone, 1, 0),
55
56 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL),
57 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
58 };
59
60 static const struct sock_fprog strict = {
61 .len = (unsigned short)ARRAY_SIZE(strict_filter),
62 .filter = (struct sock_filter *)strict_filter
63 };
64
65 static void check_strict_mode(int);
66 static void check_filter_mode(int);
67
68 static struct tcase {
69 void (*func_check)();
70 int pass_flag;
71 int val;
72 int exp_signal;
73 char *message;
74 } tcases[] = {
75 {check_strict_mode, 1, 1, SIGKILL,
76 "SECCOMP_MODE_STRICT doesn't permit GET_SECCOMP call"},
77
78 {check_strict_mode, 0, 2, SIGKILL,
79 "SECCOMP_MODE_STRICT doesn't permit read(2) write(2) and _exit(2)"},
80
81 {check_strict_mode, 1, 3, SIGKILL,
82 "SECCOMP_MODE_STRICT doesn't permit close(2)"},
83
84 {check_filter_mode, 1, 1, SIGSYS,
85 "SECCOMP_MODE_FILTER doestn't permit GET_SECCOMP call"},
86
87 {check_filter_mode, 0, 2, SIGSYS,
88 "SECCOMP_MODE_FILTER doesn't permit close(2)"},
89
90 {check_filter_mode, 2, 3, SIGSYS,
91 "SECCOMP_MODE_FILTER doesn't permit exit()"},
92
93 {check_filter_mode, 0, 4, SIGSYS,
94 "SECCOMP_MODE_FILTER doesn't permit exit()"}
95 };
96
97
98 static int mode_filter_not_supported;
99
check_filter_mode_inherit(void)100 static void check_filter_mode_inherit(void)
101 {
102 int childpid;
103 int childstatus;
104
105 childpid = SAFE_FORK();
106 if (childpid == 0) {
107 tst_res(TPASS, "SECCOMP_MODE_FILTER permits fork(2)");
108 exit(0);
109 }
110
111 wait4(childpid, &childstatus, 0, NULL);
112 if (WIFSIGNALED(childstatus) && WTERMSIG(childstatus) == SIGSYS)
113 tst_res(TPASS,
114 "SECCOMP_MODE_FILTER has been inherited by child");
115 else
116 tst_res(TFAIL,
117 "SECCOMP_MODE_FILTER isn't been inherited by child");
118 }
119
check_strict_mode(int val)120 static void check_strict_mode(int val)
121 {
122 int fd;
123 char buf[2];
124
125 fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666);
126
127 TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT));
128 if (TST_RET == -1) {
129 tst_res(TFAIL | TTERRNO,
130 "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT failed");
131 return;
132 }
133
134 switch (val) {
135 case 1:
136 tst_res(TPASS,
137 "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_STRICT succeed");
138 prctl(PR_GET_SECCOMP);
139 tst_res(TFAIL, "prctl(PR_GET_SECCOMP) succeed unexpectedly");
140 break;
141 case 2:
142 SAFE_WRITE(SAFE_WRITE_ALL, fd, "a", 1);
143 SAFE_READ(0, fd, buf, 1);
144 tst_res(TPASS,
145 "SECCOMP_MODE_STRICT permits read(2) write(2) and _exit(2)");
146 break;
147 case 3:
148 close(fd);
149 tst_res(TFAIL,
150 "SECCOMP_MODE_STRICT permits close(2) unexpectdly");
151 break;
152 }
153
154 tst_syscall(__NR_exit, 0);
155 }
156
check_filter_mode(int val)157 static void check_filter_mode(int val)
158 {
159 int fd;
160
161 if (mode_filter_not_supported == 1) {
162 tst_res(TCONF, "kernel doesn't support SECCOMP_MODE_FILTER");
163 return;
164 }
165
166 fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0666);
167
168 TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &strict));
169 if (TST_RET == -1) {
170 tst_res(TFAIL | TERRNO,
171 "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_FILTER failed");
172 return;
173 }
174
175 switch (val) {
176 case 1:
177 tst_res(TPASS,
178 "prctl(PR_SET_SECCOMP) sets SECCOMP_MODE_FILTER succeed");
179 prctl(PR_GET_SECCOMP);
180 tst_res(TFAIL, "prctl(PR_GET_SECCOMP) succeed unexpectedly");
181 break;
182 case 2:
183 close(fd);
184 tst_res(TPASS, "SECCOMP_MODE_FILTER permits close(2)");
185 break;
186 case 3:
187 exit(0);
188 break;
189 case 4:
190 check_filter_mode_inherit();
191 break;
192 }
193
194 tst_syscall(__NR_exit, 0);
195 }
196
verify_prctl(unsigned int n)197 static void verify_prctl(unsigned int n)
198 {
199 int pid;
200 int status;
201 struct tcase *tc = &tcases[n];
202
203 pid = SAFE_FORK();
204 if (pid == 0) {
205 tc->func_check(tc->val);
206 } else {
207 SAFE_WAITPID(pid, &status, 0);
208 if (WIFSIGNALED(status) && WTERMSIG(status) == tc->exp_signal) {
209 if (tc->pass_flag)
210 tst_res(TPASS, "%s", tc->message);
211 else
212 tst_res(TFAIL, "%s", tc->message);
213 return;
214 }
215
216 if (tc->pass_flag == 2 && mode_filter_not_supported == 0)
217 tst_res(TFAIL,
218 "SECCOMP_MODE_FILTER permits exit() unexpectedly");
219 }
220 }
221
setup(void)222 static void setup(void)
223 {
224 TEST(prctl(PR_GET_SECCOMP));
225 if (TST_RET == 0) {
226 tst_res(TINFO, "kernel supports PR_GET/SET_SECCOMP");
227
228 TEST(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL));
229 if (TST_RET == -1 && TST_ERR == EINVAL) {
230 mode_filter_not_supported = 1;
231 return;
232 }
233
234 tst_res(TINFO, "kernel supports SECCOMP_MODE_FILTER");
235 return;
236 }
237
238 if (TST_ERR == EINVAL)
239 tst_brk(TCONF, "kernel doesn't support PR_GET/SET_SECCOMP");
240
241 tst_brk(TBROK | TTERRNO,
242 "current environment doesn't permit PR_GET/SET_SECCOMP");
243 }
244
245 static struct tst_test test = {
246 .setup = setup,
247 .test = verify_prctl,
248 .tcnt = ARRAY_SIZE(tcases),
249 .forks_child = 1,
250 .needs_tmpdir = 1,
251 .needs_root = 1,
252 };
253