@sysid/sandbox-runtime-improved 0.0.43-sysid.6 → 0.0.45-sysid.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/dist/sandbox/linux-sandbox-utils.d.ts +17 -6
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.js +109 -16
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.js +9 -0
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.js +3 -2
- package/dist/sandbox/sandbox-manager.js.map +1 -1
- package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/x64/unix-block.bpf +0 -0
- package/dist/vendor/seccomp-src/apply-seccomp.c +231 -44
- package/dist/vendor/seccomp-src/seccomp-unix-block.c +50 -3
- package/package.json +2 -2
- package/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/vendor/seccomp/x64/apply-seccomp +0 -0
- package/vendor/seccomp/x64/unix-block.bpf +0 -0
- package/vendor/seccomp-src/apply-seccomp.c +231 -44
- package/vendor/seccomp-src/seccomp-unix-block.c +50 -3
|
@@ -1,39 +1,149 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* apply-seccomp.c - Apply seccomp BPF filter
|
|
2
|
+
* apply-seccomp.c - Apply seccomp BPF filter in an isolated PID namespace
|
|
3
3
|
*
|
|
4
4
|
* Usage: apply-seccomp <filter.bpf> <command> [args...]
|
|
5
5
|
*
|
|
6
|
-
* This program reads a pre-compiled BPF filter from a file,
|
|
7
|
-
*
|
|
6
|
+
* This program reads a pre-compiled BPF filter from a file, isolates the
|
|
7
|
+
* target command in a nested user+PID+mount namespace so it cannot see or
|
|
8
|
+
* ptrace any process that lacks the filter, applies the filter with
|
|
9
|
+
* prctl(PR_SET_SECCOMP), and execs the command.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* Process layout inside the outer bwrap sandbox:
|
|
12
|
+
*
|
|
13
|
+
* bwrap init (PID 1) <- outer PID ns, no seccomp
|
|
14
|
+
* \_ bash / socat ... <- outer PID ns, no seccomp
|
|
15
|
+
* \_ apply-seccomp [outer] <- outer PID ns, waits for inner init
|
|
16
|
+
* ================================================= PID ns boundary
|
|
17
|
+
* \_ apply-seccomp [inner init] <- inner PID 1, PR_SET_DUMPABLE=0
|
|
18
|
+
* \_ user command <- inner PID 2, seccomp applied
|
|
19
|
+
*
|
|
20
|
+
* From the user command's point of view /proc contains only its own process
|
|
21
|
+
* tree. The bwrap init, bash wrapper, and socat helpers are not addressable,
|
|
22
|
+
* so they cannot be ptraced or patched via /proc/N/mem even on systems with
|
|
23
|
+
* kernel.yama.ptrace_scope=0. The inner init (PID 1) sets PR_SET_DUMPABLE=0
|
|
24
|
+
* so it cannot be ptraced either.
|
|
25
|
+
*
|
|
26
|
+
* Any failure to set up the nested namespaces aborts with a non-zero exit
|
|
27
|
+
* status; we never fall back to running the command without isolation.
|
|
12
28
|
*
|
|
13
29
|
* Compile: gcc -static -O2 -o apply-seccomp apply-seccomp.c
|
|
14
30
|
*/
|
|
15
31
|
|
|
32
|
+
#define _GNU_SOURCE
|
|
16
33
|
#include <stdio.h>
|
|
17
34
|
#include <stdlib.h>
|
|
35
|
+
#include <stdarg.h>
|
|
18
36
|
#include <string.h>
|
|
19
37
|
#include <unistd.h>
|
|
20
38
|
#include <fcntl.h>
|
|
39
|
+
#include <errno.h>
|
|
40
|
+
#include <sched.h>
|
|
41
|
+
#include <signal.h>
|
|
21
42
|
#include <sys/prctl.h>
|
|
43
|
+
#include <sys/wait.h>
|
|
44
|
+
#include <sys/mount.h>
|
|
22
45
|
#include <linux/seccomp.h>
|
|
23
46
|
#include <linux/filter.h>
|
|
24
|
-
#include <errno.h>
|
|
25
47
|
|
|
26
48
|
#ifndef PR_SET_NO_NEW_PRIVS
|
|
27
49
|
#define PR_SET_NO_NEW_PRIVS 38
|
|
28
50
|
#endif
|
|
29
51
|
|
|
52
|
+
#ifndef PR_CAP_AMBIENT
|
|
53
|
+
#define PR_CAP_AMBIENT 47
|
|
54
|
+
#define PR_CAP_AMBIENT_CLEAR_ALL 4
|
|
55
|
+
#endif
|
|
56
|
+
|
|
30
57
|
#ifndef SECCOMP_MODE_FILTER
|
|
31
58
|
#define SECCOMP_MODE_FILTER 2
|
|
32
59
|
#endif
|
|
33
60
|
|
|
34
|
-
#define MAX_FILTER_SIZE 4096
|
|
61
|
+
#define MAX_FILTER_SIZE 4096
|
|
62
|
+
|
|
63
|
+
static void die(const char *msg) {
|
|
64
|
+
perror(msg);
|
|
65
|
+
_exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static int write_file(const char *path, const char *fmt, ...) {
|
|
69
|
+
char buf[256];
|
|
70
|
+
va_list ap;
|
|
71
|
+
va_start(ap, fmt);
|
|
72
|
+
int len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
73
|
+
va_end(ap);
|
|
74
|
+
if (len < 0 || (size_t)len >= sizeof(buf)) {
|
|
75
|
+
errno = EOVERFLOW;
|
|
76
|
+
return -1;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
int fd = open(path, O_WRONLY);
|
|
80
|
+
if (fd < 0) {
|
|
81
|
+
return -1;
|
|
82
|
+
}
|
|
83
|
+
ssize_t r = write(fd, buf, (size_t)len);
|
|
84
|
+
int saved = errno;
|
|
85
|
+
close(fd);
|
|
86
|
+
if (r != len) {
|
|
87
|
+
errno = (r < 0) ? saved : EIO;
|
|
88
|
+
return -1;
|
|
89
|
+
}
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* PID the current process forwards signals to. Used by both the outer stub
|
|
94
|
+
* (forwards to inner init) and the inner init (forwards to the worker).
|
|
95
|
+
* PID 1 ignores signals it has no handler for, so the inner init MUST install
|
|
96
|
+
* these or SIGTERM from the outside is silently dropped. */
|
|
97
|
+
static volatile pid_t forward_target = -1;
|
|
98
|
+
|
|
99
|
+
static void forward_signal(int sig) {
|
|
100
|
+
if (forward_target > 0) {
|
|
101
|
+
kill(forward_target, sig);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static void install_forwarders(pid_t target) {
|
|
106
|
+
forward_target = target;
|
|
107
|
+
struct sigaction sa = { .sa_handler = forward_signal };
|
|
108
|
+
sigemptyset(&sa.sa_mask);
|
|
109
|
+
sigaction(SIGTERM, &sa, NULL);
|
|
110
|
+
sigaction(SIGINT, &sa, NULL);
|
|
111
|
+
sigaction(SIGHUP, &sa, NULL);
|
|
112
|
+
sigaction(SIGQUIT, &sa, NULL);
|
|
113
|
+
sigaction(SIGUSR1, &sa, NULL);
|
|
114
|
+
sigaction(SIGUSR2, &sa, NULL);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* Wait for `main_child`, reaping any other children that exit first.
|
|
119
|
+
* Returns as soon as `main_child` terminates — the caller then _exit()s,
|
|
120
|
+
* which as PID 1 tears down the namespace and SIGKILLs any stragglers.
|
|
121
|
+
* Returns an exit(3)-style status: exit code, or 128+signal.
|
|
122
|
+
*/
|
|
123
|
+
static int reap_until(pid_t main_child) {
|
|
124
|
+
int status = 0;
|
|
125
|
+
for (;;) {
|
|
126
|
+
pid_t r = waitpid(-1, &status, 0);
|
|
127
|
+
if (r < 0) {
|
|
128
|
+
if (errno == EINTR) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
return 1; /* ECHILD without seeing main_child — shouldn't happen. */
|
|
132
|
+
}
|
|
133
|
+
if (r == main_child) {
|
|
134
|
+
if (WIFEXITED(status)) {
|
|
135
|
+
return WEXITSTATUS(status);
|
|
136
|
+
}
|
|
137
|
+
if (WIFSIGNALED(status)) {
|
|
138
|
+
return 128 + WTERMSIG(status);
|
|
139
|
+
}
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
/* Reaped an orphan that died before main_child; keep waiting. */
|
|
143
|
+
}
|
|
144
|
+
}
|
|
35
145
|
|
|
36
|
-
int main(int argc, char *argv[]
|
|
146
|
+
int main(int argc, char *argv[]) {
|
|
37
147
|
if (argc < 3) {
|
|
38
148
|
fprintf(stderr, "Usage: %s <filter.bpf> <command> [args...]\n", argv[0]);
|
|
39
149
|
return 1;
|
|
@@ -42,57 +152,134 @@ int main(int argc, char *argv[], char *envp[]) {
|
|
|
42
152
|
const char *filter_path = argv[1];
|
|
43
153
|
char **command_argv = &argv[2];
|
|
44
154
|
|
|
45
|
-
|
|
155
|
+
/* ---- Load the BPF filter up front so we fail before any namespace work. ---- */
|
|
46
156
|
int fd = open(filter_path, O_RDONLY);
|
|
47
157
|
if (fd < 0) {
|
|
48
|
-
|
|
49
|
-
return 1;
|
|
158
|
+
die("apply-seccomp: open(filter)");
|
|
50
159
|
}
|
|
51
|
-
|
|
52
|
-
// Read filter into memory
|
|
53
|
-
unsigned char filter_bytes[MAX_FILTER_SIZE];
|
|
160
|
+
static unsigned char filter_bytes[MAX_FILTER_SIZE];
|
|
54
161
|
ssize_t filter_size = read(fd, filter_bytes, MAX_FILTER_SIZE);
|
|
55
162
|
close(fd);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
perror("Failed to read BPF filter");
|
|
163
|
+
if (filter_size <= 0 || filter_size % 8 != 0) {
|
|
164
|
+
fprintf(stderr, "apply-seccomp: invalid BPF filter (size=%zd)\n", filter_size);
|
|
59
165
|
return 1;
|
|
60
166
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
167
|
+
struct sock_fprog prog = {
|
|
168
|
+
.len = (unsigned short)(filter_size / 8),
|
|
169
|
+
.filter = (struct sock_filter *)filter_bytes,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/* ---- New PID + mount namespaces. Children (not us) enter the PID ns. ----
|
|
173
|
+
*
|
|
174
|
+
* Two paths to get CAP_SYS_ADMIN for the unshare:
|
|
175
|
+
* (a) The caller (bwrap) kept CAP_SYS_ADMIN in this user namespace via
|
|
176
|
+
* --cap-add. Just unshare directly.
|
|
177
|
+
* (b) We don't have the cap. Create a nested user namespace to get it,
|
|
178
|
+
* map uid/gid, then unshare. This also works when apply-seccomp is
|
|
179
|
+
* run standalone outside bwrap.
|
|
180
|
+
*
|
|
181
|
+
* Path (a) is tried first. If the caller didn't give us the cap, the
|
|
182
|
+
* kernel returns EPERM and we fall through to (b). Path (b) can itself
|
|
183
|
+
* fail on hosts where unprivileged user namespaces are gated by an LSM
|
|
184
|
+
* (Ubuntu 24.04's AppArmor restriction, for example) — the unshare
|
|
185
|
+
* succeeds but the new namespace grants no capabilities, so the setgroups
|
|
186
|
+
* write fails. In that case we abort: the caller must supply CAP_SYS_ADMIN.
|
|
187
|
+
*/
|
|
188
|
+
if (unshare(CLONE_NEWPID | CLONE_NEWNS) < 0) {
|
|
189
|
+
if (errno != EPERM) {
|
|
190
|
+
die("apply-seccomp: unshare(CLONE_NEWPID|CLONE_NEWNS)");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
uid_t uid = geteuid();
|
|
194
|
+
gid_t gid = getegid();
|
|
195
|
+
|
|
196
|
+
if (unshare(CLONE_NEWUSER) < 0) {
|
|
197
|
+
die("apply-seccomp: unshare(CLONE_NEWUSER)");
|
|
198
|
+
}
|
|
199
|
+
if (write_file("/proc/self/setgroups", "deny") < 0) {
|
|
200
|
+
die("apply-seccomp: write /proc/self/setgroups "
|
|
201
|
+
"(nested userns is capability-restricted; "
|
|
202
|
+
"caller must provide CAP_SYS_ADMIN)");
|
|
203
|
+
}
|
|
204
|
+
if (write_file("/proc/self/uid_map", "%u %u 1\n", uid, uid) < 0) {
|
|
205
|
+
die("apply-seccomp: write /proc/self/uid_map");
|
|
206
|
+
}
|
|
207
|
+
if (write_file("/proc/self/gid_map", "%u %u 1\n", gid, gid) < 0) {
|
|
208
|
+
die("apply-seccomp: write /proc/self/gid_map");
|
|
209
|
+
}
|
|
210
|
+
if (unshare(CLONE_NEWPID | CLONE_NEWNS) < 0) {
|
|
211
|
+
die("apply-seccomp: unshare(CLONE_NEWPID|CLONE_NEWNS) after userns");
|
|
212
|
+
}
|
|
64
213
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
214
|
+
|
|
215
|
+
pid_t child = fork();
|
|
216
|
+
if (child < 0) {
|
|
217
|
+
die("apply-seccomp: fork");
|
|
68
218
|
}
|
|
69
219
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
220
|
+
if (child > 0) {
|
|
221
|
+
/* Outer stub: still in bwrap's PID namespace. Forward signals and
|
|
222
|
+
* wait so the caller sees the real exit status. */
|
|
223
|
+
install_forwarders(child);
|
|
73
224
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
225
|
+
int status;
|
|
226
|
+
for (;;) {
|
|
227
|
+
pid_t r = waitpid(child, &status, 0);
|
|
228
|
+
if (r < 0 && errno == EINTR) continue;
|
|
229
|
+
if (r < 0) die("apply-seccomp: waitpid");
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
|
|
233
|
+
return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
|
|
234
|
+
}
|
|
79
235
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
236
|
+
/* ================================================================
|
|
237
|
+
* Inner init — PID 1 in the nested PID namespace.
|
|
238
|
+
* ================================================================ */
|
|
239
|
+
|
|
240
|
+
/* Block ptrace and /proc/1/mem writes against this process. */
|
|
241
|
+
if (prctl(PR_SET_DUMPABLE, 0) < 0) {
|
|
242
|
+
die("apply-seccomp: prctl(PR_SET_DUMPABLE)");
|
|
84
243
|
}
|
|
85
244
|
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
245
|
+
/* Don't let our /proc mount propagate anywhere. */
|
|
246
|
+
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) {
|
|
247
|
+
die("apply-seccomp: mount(MS_PRIVATE)");
|
|
248
|
+
}
|
|
249
|
+
if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL) < 0) {
|
|
250
|
+
die("apply-seccomp: mount(/proc)");
|
|
90
251
|
}
|
|
91
252
|
|
|
92
|
-
|
|
93
|
-
|
|
253
|
+
/* bwrap --cap-add places CAP_SYS_ADMIN in the ambient set so it survives
|
|
254
|
+
* exec. Clear it now that the mount is done; combined with
|
|
255
|
+
* PR_SET_NO_NEW_PRIVS, the worker's execve drops to zero capabilities. */
|
|
256
|
+
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) < 0) {
|
|
257
|
+
die("apply-seccomp: prctl(PR_CAP_AMBIENT_CLEAR_ALL)");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Fork the real workload so PID 1 can stay as a non-dumpable reaper. */
|
|
261
|
+
pid_t worker = fork();
|
|
262
|
+
if (worker < 0) {
|
|
263
|
+
die("apply-seccomp: fork(worker)");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (worker > 0) {
|
|
267
|
+
/* Inner init: reap everything, exit with the worker's status.
|
|
268
|
+
* When PID 1 exits the kernel tears down the whole namespace.
|
|
269
|
+
* PID 1 drops signals without handlers, so install forwarders. */
|
|
270
|
+
install_forwarders(worker);
|
|
271
|
+
_exit(reap_until(worker));
|
|
272
|
+
}
|
|
94
273
|
|
|
95
|
-
|
|
96
|
-
|
|
274
|
+
/* ---- Worker (inner PID 2): apply seccomp and exec. ---- */
|
|
275
|
+
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
|
|
276
|
+
die("apply-seccomp: prctl(PR_SET_NO_NEW_PRIVS)");
|
|
277
|
+
}
|
|
278
|
+
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
|
|
279
|
+
die("apply-seccomp: prctl(PR_SET_SECCOMP)");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
execvp(command_argv[0], command_argv);
|
|
283
|
+
die("apply-seccomp: execvp");
|
|
97
284
|
return 1;
|
|
98
285
|
}
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
* gcc -o seccomp-unix-block seccomp-unix-block.c -lseccomp
|
|
28
28
|
*
|
|
29
29
|
* Usage:
|
|
30
|
-
* ./seccomp-unix-block <output-file>
|
|
30
|
+
* ./seccomp-unix-block <output-file> [arch]
|
|
31
|
+
*
|
|
32
|
+
* If arch is given (x86_64 or aarch64), the filter is generated for that
|
|
33
|
+
* architecture instead of the native one. Lets a single-arch builder emit
|
|
34
|
+
* filters for both x64 and arm64.
|
|
31
35
|
*
|
|
32
36
|
* Dependencies:
|
|
33
37
|
* - libseccomp (libseccomp-dev package on Debian/Ubuntu)
|
|
@@ -48,12 +52,13 @@ int main(int argc, char *argv[]) {
|
|
|
48
52
|
scmp_filter_ctx ctx;
|
|
49
53
|
int rc;
|
|
50
54
|
|
|
51
|
-
if (argc
|
|
52
|
-
fprintf(stderr, "Usage: %s <output-file
|
|
55
|
+
if (argc < 2 || argc > 3) {
|
|
56
|
+
fprintf(stderr, "Usage: %s <output-file> [x86_64|aarch64]\n", argv[0]);
|
|
53
57
|
return 1;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
const char *output_file = argv[1];
|
|
61
|
+
const char *arch_name = (argc == 3) ? argv[2] : NULL;
|
|
57
62
|
|
|
58
63
|
/* Create seccomp context with default action ALLOW */
|
|
59
64
|
ctx = seccomp_init(SCMP_ACT_ALLOW);
|
|
@@ -62,6 +67,28 @@ int main(int argc, char *argv[]) {
|
|
|
62
67
|
return 1;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
if (arch_name != NULL) {
|
|
71
|
+
uint32_t target;
|
|
72
|
+
if (strcmp(arch_name, "x86_64") == 0) {
|
|
73
|
+
target = SCMP_ARCH_X86_64;
|
|
74
|
+
} else if (strcmp(arch_name, "aarch64") == 0) {
|
|
75
|
+
target = SCMP_ARCH_AARCH64;
|
|
76
|
+
} else {
|
|
77
|
+
fprintf(stderr, "Error: Unsupported arch '%s'\n", arch_name);
|
|
78
|
+
seccomp_release(ctx);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
if (target != seccomp_arch_native()) {
|
|
82
|
+
rc = seccomp_arch_remove(ctx, SCMP_ARCH_NATIVE);
|
|
83
|
+
if (rc == 0) rc = seccomp_arch_add(ctx, target);
|
|
84
|
+
if (rc < 0) {
|
|
85
|
+
fprintf(stderr, "Error: Failed to set target arch: %s\n", strerror(-rc));
|
|
86
|
+
seccomp_release(ctx);
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
65
92
|
/* Add rule to block socket(AF_UNIX, ...) */
|
|
66
93
|
/* socket() syscall signature: int socket(int domain, int type, int protocol) */
|
|
67
94
|
/* arg0 = domain (AF_UNIX = 1) */
|
|
@@ -77,6 +104,26 @@ int main(int argc, char *argv[]) {
|
|
|
77
104
|
return 1;
|
|
78
105
|
}
|
|
79
106
|
|
|
107
|
+
/* Block io_uring entirely. IORING_OP_SOCKET (Linux 5.19+) creates sockets
|
|
108
|
+
* in kernel context without going through the socket() syscall, bypassing
|
|
109
|
+
* the rule above. seccomp cannot inspect io_uring SQEs (they live in a
|
|
110
|
+
* shared-memory ring), so the only safe option is to deny ring creation
|
|
111
|
+
* and use. Blocking all three syscalls also covers the case of an
|
|
112
|
+
* inherited ring fd. */
|
|
113
|
+
int io_uring_calls[] = {
|
|
114
|
+
SCMP_SYS(io_uring_setup),
|
|
115
|
+
SCMP_SYS(io_uring_enter),
|
|
116
|
+
SCMP_SYS(io_uring_register),
|
|
117
|
+
};
|
|
118
|
+
for (size_t i = 0; i < sizeof(io_uring_calls) / sizeof(io_uring_calls[0]); i++) {
|
|
119
|
+
rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), io_uring_calls[i], 0);
|
|
120
|
+
if (rc < 0) {
|
|
121
|
+
fprintf(stderr, "Error: Failed to add io_uring rule: %s\n", strerror(-rc));
|
|
122
|
+
seccomp_release(ctx);
|
|
123
|
+
return 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
80
127
|
/* Export the filter to a file */
|
|
81
128
|
int fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
|
82
129
|
if (fd < 0) {
|