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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <fcntl.h>
#include <linux/unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
#include "sandbox/linux/seccomp-bpf/util.h"
#define ERR EPERM
// We don't expect our sandbox to do anything useful yet. So, we will fail
// almost immediately. For now, force the code to continue running. The
// following line should be removed as soon as the sandbox is starting to
// actually enforce restrictions in a meaningful way:
#define _exit(x) do { } while (0)
static playground2::Sandbox::ErrorCode evaluator(int sysno) {
switch (sysno) {
#if defined(__NR_accept)
case __NR_accept: case __NR_accept4:
#endif
case __NR_alarm:
case __NR_brk:
case __NR_clock_gettime:
case __NR_close:
case __NR_dup: case __NR_dup2:
case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait:
case __NR_exit: case __NR_exit_group:
case __NR_fcntl:
#if defined(__NR_fcntl64)
case __NR_fcntl64:
#endif
case __NR_fdatasync:
case __NR_fstat:
#if defined(__NR_fstat64)
case __NR_fstat64:
#endif
case __NR_ftruncate:
case __NR_futex:
case __NR_getdents: case __NR_getdents64:
case __NR_getegid:
#if defined(__NR_getegid32)
case __NR_getegid32:
#endif
case __NR_geteuid:
#if defined(__NR_geteuid32)
case __NR_geteuid32:
#endif
case __NR_getgid:
#if defined(__NR_getgid32)
case __NR_getgid32:
#endif
case __NR_getitimer: case __NR_setitimer:
#if defined(__NR_getpeername)
case __NR_getpeername:
#endif
case __NR_getpid: case __NR_gettid:
#if defined(__NR_getsockname)
case __NR_getsockname:
#endif
case __NR_gettimeofday:
case __NR_getuid:
#if defined(__NR_getuid32)
case __NR_getuid32:
#endif
#if defined(__NR__llseek)
case __NR__llseek:
#endif
case __NR_lseek:
case __NR_nanosleep:
case __NR_pipe: case __NR_pipe2:
case __NR_poll:
case __NR_pread64: case __NR_preadv:
case __NR_pwrite64: case __NR_pwritev:
case __NR_read: case __NR_readv:
case __NR_restart_syscall:
case __NR_set_robust_list:
case __NR_rt_sigaction:
#if defined(__NR_sigaction)
case __NR_sigaction:
#endif
#if defined(__NR_signal)
case __NR_signal:
#endif
case __NR_rt_sigprocmask:
#if defined(__NR_sigprocmask)
case __NR_sigprocmask:
#endif
#if defined(__NR_shutdown)
case __NR_shutdown:
#endif
case __NR_rt_sigreturn:
#if defined(__NR_sigreturn)
case __NR_sigreturn:
#endif
#if defined(__NR_socketpair)
case __NR_socketpair:
#endif
case __NR_time:
case __NR_uname:
case __NR_write: case __NR_writev:
return playground2::Sandbox::SB_ALLOWED;
// The following system calls are temporarily permitted. This must be
// tightened later. But we currently don't implement enough of the sandboxing
// API to do so.
// As is, this sandbox isn't exactly safe :-/
#if defined(__NR_sendmsg)
case __NR_sendmsg: case __NR_sendto:
case __NR_recvmsg: case __NR_recvfrom:
case __NR_getsockopt: case __NR_setsockopt:
#elif defined(__NR_socketcall)
case __NR_socketcall:
#endif
#if defined(__NR_shmat)
case __NR_shmat: case __NR_shmctl: case __NR_shmdt: case __NR_shmget:
#elif defined(__NR_ipc)
case __NR_ipc:
#endif
#if defined(__NR_mmap2)
case __NR_mmap2:
#else
case __NR_mmap:
#endif
#if defined(__NR_ugetrlimit)
case __NR_ugetrlimit:
#endif
case __NR_getrlimit:
case __NR_ioctl:
case __NR_prctl:
case __NR_clone:
case __NR_munmap: case __NR_mprotect: case __NR_madvise:
case __NR_remap_file_pages:
return playground2::Sandbox::SB_ALLOWED;
// Everything that isn't explicitly allowed is denied.
default:
return (playground2::Sandbox::ErrorCode)ERR;
}
}
static void *threadFnc(void *arg) {
return arg;
}
static void *sendmsgStressThreadFnc(void *arg) {
static const int repetitions = 100;
static const int kNumFds = 3;
for (int rep = 0; rep < repetitions; ++rep) {
int fds[2 + kNumFds];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
perror("socketpair()");
_exit(1);
}
size_t len = 4;
char buf[4];
if (!playground2::Util::sendFds(fds[0], "test", 4,
fds[1], fds[1], fds[1], -1) ||
!playground2::Util::getFds(fds[1], buf, &len,
fds+2, fds+3, fds+4, NULL) ||
len != 4 ||
memcmp(buf, "test", len) ||
write(fds[2], "demo", 4) != 4 ||
read(fds[0], buf, 4) != 4 ||
memcmp(buf, "demo", 4)) {
perror("sending/receiving of fds");
_exit(1);
}
for (int i = 0; i < 2+kNumFds; ++i) {
if (close(fds[i])) {
perror("close");
_exit(1);
}
}
}
return NULL;
}
int main(int argc, char *argv[]) {
int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY);
if (playground2::Sandbox::supportsSeccompSandbox(proc_fd) !=
playground2::Sandbox::STATUS_AVAILABLE) {
perror("sandbox");
_exit(1);
}
playground2::Sandbox::setProcFd(proc_fd);
playground2::Sandbox::setSandboxPolicy(evaluator, NULL);
playground2::Sandbox::startSandbox();
// Check that we can create threads
pthread_t thr;
if (!pthread_create(&thr, NULL, threadFnc,
reinterpret_cast<void *>(0x1234))) {
void *ret;
pthread_join(thr, &ret);
if (ret != reinterpret_cast<void *>(0x1234)) {
perror("clone() failed");
_exit(1);
}
} else {
perror("clone() failed");
_exit(1);
}
// Check that we handle restart_syscall() without dieing. This is a little
// tricky to trigger. And I can't think of a good way to verify whether it
// actually executed.
signal(SIGALRM, SIG_IGN);
const struct itimerval tv = { { 0, 0 }, { 0, 5*1000 } };
const struct timespec tmo = { 0, 100*1000*1000 };
setitimer(ITIMER_REAL, &tv, NULL);
nanosleep(&tmo, NULL);
// Check that we can query the size of the stack, but that all other
// calls to getrlimit() fail.
if (((errno = 0), !getrlimit(RLIMIT_STACK, NULL)) || errno != EFAULT ||
((errno = 0), !getrlimit(RLIMIT_CORE, NULL)) || errno != ERR) {
perror("getrlimit()");
_exit(1);
}
// Check that we can query TCGETS and TIOCGWINSZ, but no other ioctls().
if (((errno = 0), !ioctl(2, TCGETS, NULL)) || errno != EFAULT ||
((errno = 0), !ioctl(2, TIOCGWINSZ, NULL)) || errno != EFAULT ||
((errno = 0), !ioctl(2, TCSETS, NULL)) || errno != ERR) {
perror("ioctl()");
_exit(1);
}
// Check that prctl() can manipulate the dumpable flag, but nothing else.
if (((errno = 0), !prctl(PR_GET_DUMPABLE)) || errno ||
((errno = 0), prctl(PR_SET_DUMPABLE, 1)) || errno ||
((errno = 0), !prctl(PR_SET_SECCOMP, 0)) || errno != ERR) {
perror("prctl()");
_exit(1);
}
// Check that we can send and receive file handles.
int fds[3];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
perror("socketpair()");
_exit(1);
}
size_t len = 4;
char buf[4];
if (!playground2::Util::sendFds(fds[0], "test", 4, fds[1], -1) ||
!playground2::Util::getFds(fds[1], buf, &len, fds+2, NULL) ||
len != 4 ||
memcmp(buf, "test", len) ||
write(fds[2], "demo", 4) != 4 ||
read(fds[0], buf, 4) != 4 ||
memcmp(buf, "demo", 4) ||
close(fds[0]) ||
close(fds[1]) ||
close(fds[2])) {
perror("sending/receiving of fds");
_exit(1);
}
// Check whether SysV IPC works.
int shmid;
void *addr;
if ((shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|0600)) < 0 ||
(addr = shmat(shmid, NULL, 0)) == reinterpret_cast<void *>(-1) ||
shmdt(addr) ||
shmctl(shmid, IPC_RMID, NULL)) {
perror("sysv IPC");
_exit(1);
}
// Print a message so that the user can see the sandbox is activated.
time_t tm = time(NULL);
printf("Sandbox has been started at %s", ctime(&tm));
// Stress-test the sendmsg() code
static const int kSendmsgStressNumThreads = 10;
pthread_t sendmsgStressThreads[kSendmsgStressNumThreads];
for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
if (pthread_create(sendmsgStressThreads + i, NULL,
sendmsgStressThreadFnc, NULL)) {
perror("pthread_create");
_exit(1);
}
}
for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
pthread_join(sendmsgStressThreads[i], NULL);
}
return 0;
}
|