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