@@ -120,7 +120,7 @@ static interface LinuxLibrary extends Library {
120
120
static final int PR_SET_NO_NEW_PRIVS = 38 ; // since Linux 3.5
121
121
static final int PR_GET_SECCOMP = 21 ; // since Linux 2.6.23
122
122
static final int PR_SET_SECCOMP = 22 ; // since Linux 2.6.23
123
- static final int SECCOMP_MODE_FILTER = 2 ; // since Linux Linux 3.5
123
+ static final long SECCOMP_MODE_FILTER = 2 ; // since Linux Linux 3.5
124
124
125
125
/** corresponds to struct sock_filter */
126
126
static final class SockFilter {
@@ -209,9 +209,10 @@ static SockFilter BPF_JUMP(int code, int k, int jt, int jf) {
209
209
static final int NR_SYSCALL_FORK = 57 ;
210
210
static final int NR_SYSCALL_EXECVE = 59 ;
211
211
static final int NR_SYSCALL_EXECVEAT = 322 ; // since Linux 3.19
212
+ static final int NR_SYSCALL_TUXCALL = 184 ; // should return ENOSYS
212
213
213
214
/** try to install our BPF filters via seccomp() or prctl() to block execution */
214
- private static void linuxImpl () {
215
+ private static int linuxImpl () {
215
216
// first be defensive: we can give nice errors this way, at the very least.
216
217
// also, some of these security features get backported to old versions, checking kernel version here is a big no-no!
217
218
boolean supported = Constants .LINUX && "amd64" .equals (Constants .OS_ARCH );
@@ -224,24 +225,85 @@ private static void linuxImpl() {
224
225
throw new UnsupportedOperationException ("seccomp unavailable: could not link methods. requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in" );
225
226
}
226
227
227
- // check for kernel version
228
- if (linux_libc .prctl (PR_GET_NO_NEW_PRIVS , 0 , 0 , 0 , 0 ) < 0 ) {
228
+ // pure paranoia:
229
+
230
+ // check that unimplemented syscalls actually return ENOSYS
231
+ // you never know (e.g. https://code.google.com/p/chromium/issues/detail?id=439795)
232
+ if (linux_libc .syscall (NR_SYSCALL_TUXCALL ) >= 0 || Native .getLastError () != ENOSYS ) {
233
+ throw new UnsupportedOperationException ("seccomp unavailable: your kernel is buggy and you should upgrade" );
234
+ }
235
+
236
+ // try to check system calls really are who they claim
237
+ // you never know (e.g. https://chromium.googlesource.com/chromium/src.git/+/master/sandbox/linux/seccomp-bpf/sandbox_bpf.cc#57)
238
+ final int bogusArg = 0xf7a46a5c ;
239
+
240
+ // test seccomp(BOGUS)
241
+ long ret = linux_libc .syscall (SECCOMP_SYSCALL_NR , bogusArg );
242
+ if (ret != -1 ) {
243
+ throw new UnsupportedOperationException ("seccomp unavailable: seccomp(BOGUS_OPERATION) returned " + ret );
244
+ } else {
229
245
int errno = Native .getLastError ();
230
246
switch (errno ) {
231
- case ENOSYS : throw new UnsupportedOperationException ("seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in" );
232
- default : throw new UnsupportedOperationException ("prctl(PR_GET_NO_NEW_PRIVS): " + JNACLibrary .strerror (errno ));
247
+ case ENOSYS : break ; // ok
248
+ case EINVAL : break ; // ok
249
+ default : throw new UnsupportedOperationException ("seccomp(BOGUS_OPERATION): " + JNACLibrary .strerror (errno ));
233
250
}
234
251
}
235
- // check for SECCOMP
236
- if (linux_libc .prctl (PR_GET_SECCOMP , 0 , 0 , 0 , 0 ) < 0 ) {
252
+
253
+ // test seccomp(VALID, BOGUS)
254
+ ret = linux_libc .syscall (SECCOMP_SYSCALL_NR , SECCOMP_SET_MODE_FILTER , bogusArg );
255
+ if (ret != -1 ) {
256
+ throw new UnsupportedOperationException ("seccomp unavailable: seccomp(SECCOMP_SET_MODE_FILTER, BOGUS_FLAG) returned " + ret );
257
+ } else {
237
258
int errno = Native .getLastError ();
238
259
switch (errno ) {
239
- case EINVAL : throw new UnsupportedOperationException ("seccomp unavailable: CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed" );
240
- default : throw new UnsupportedOperationException ("prctl(PR_GET_SECCOMP): " + JNACLibrary .strerror (errno ));
260
+ case ENOSYS : break ; // ok
261
+ case EINVAL : break ; // ok
262
+ default : throw new UnsupportedOperationException ("seccomp(SECCOMP_SET_MODE_FILTER, BOGUS_FLAG): " + JNACLibrary .strerror (errno ));
241
263
}
242
264
}
265
+
266
+ // test prctl(BOGUS)
267
+ ret = linux_libc .prctl (bogusArg , 0 , 0 , 0 , 0 );
268
+ if (ret != -1 ) {
269
+ throw new UnsupportedOperationException ("seccomp unavailable: prctl(BOGUS_OPTION) returned " + ret );
270
+ } else {
271
+ int errno = Native .getLastError ();
272
+ switch (errno ) {
273
+ case ENOSYS : break ; // ok
274
+ case EINVAL : break ; // ok
275
+ default : throw new UnsupportedOperationException ("prctl(BOGUS_OPTION): " + JNACLibrary .strerror (errno ));
276
+ }
277
+ }
278
+
279
+ // now just normal defensive checks
280
+
281
+ // check for GET_NO_NEW_PRIVS
282
+ switch (linux_libc .prctl (PR_GET_NO_NEW_PRIVS , 0 , 0 , 0 , 0 )) {
283
+ case 0 : break ; // not yet set
284
+ case 1 : break ; // already set by caller
285
+ default :
286
+ int errno = Native .getLastError ();
287
+ if (errno == ENOSYS ) {
288
+ throw new UnsupportedOperationException ("seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in" );
289
+ } else {
290
+ throw new UnsupportedOperationException ("prctl(PR_GET_NO_NEW_PRIVS): " + JNACLibrary .strerror (errno ));
291
+ }
292
+ }
293
+ // check for SECCOMP
294
+ switch (linux_libc .prctl (PR_GET_SECCOMP , 0 , 0 , 0 , 0 )) {
295
+ case 0 : break ; // not yet set
296
+ case 2 : break ; // already in filter mode by caller
297
+ default :
298
+ int errno = Native .getLastError ();
299
+ if (errno == EINVAL ) {
300
+ throw new UnsupportedOperationException ("seccomp unavailable: CONFIG_SECCOMP not compiled into kernel, CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER are needed" );
301
+ } else {
302
+ throw new UnsupportedOperationException ("prctl(PR_GET_SECCOMP): " + JNACLibrary .strerror (errno ));
303
+ }
304
+ }
243
305
// check for SECCOMP_MODE_FILTER
244
- if (linux_libc .prctl (PR_SET_SECCOMP , SECCOMP_MODE_FILTER , 0 , 0 , 0 ) < 0 ) {
306
+ if (linux_libc .prctl (PR_SET_SECCOMP , SECCOMP_MODE_FILTER , 0 , 0 , 0 ) != 0 ) {
245
307
int errno = Native .getLastError ();
246
308
switch (errno ) {
247
309
case EFAULT : break ; // available
@@ -251,10 +313,15 @@ private static void linuxImpl() {
251
313
}
252
314
253
315
// ok, now set PR_SET_NO_NEW_PRIVS, needed to be able to set a seccomp filter as ordinary user
254
- if (linux_libc .prctl (PR_SET_NO_NEW_PRIVS , 1 , 0 , 0 , 0 ) < 0 ) {
316
+ if (linux_libc .prctl (PR_SET_NO_NEW_PRIVS , 1 , 0 , 0 , 0 ) != 0 ) {
255
317
throw new UnsupportedOperationException ("prctl(PR_SET_NO_NEW_PRIVS): " + JNACLibrary .strerror (Native .getLastError ()));
256
318
}
257
319
320
+ // check it worked
321
+ if (linux_libc .prctl (PR_GET_NO_NEW_PRIVS , 0 , 0 , 0 , 0 ) != 1 ) {
322
+ throw new UnsupportedOperationException ("seccomp filter did not really succeed: prctl(PR_GET_NO_NEW_PRIVS): " + JNACLibrary .strerror (Native .getLastError ()));
323
+ }
324
+
258
325
// BPF installed to check arch, then syscall range. See https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt for details.
259
326
SockFilter insns [] = {
260
327
/* 1 */ BPF_STMT (BPF_LD + BPF_W + BPF_ABS , SECCOMP_DATA_ARCH_OFFSET ), //
@@ -272,14 +339,16 @@ private static void linuxImpl() {
272
339
prog .write ();
273
340
long pointer = Pointer .nativeValue (prog .getPointer ());
274
341
342
+ int method = 1 ;
275
343
// install filter, if this works, after this there is no going back!
276
344
// first try it with seccomp(SECCOMP_SET_MODE_FILTER), falling back to prctl()
277
345
if (linux_libc .syscall (SECCOMP_SYSCALL_NR , SECCOMP_SET_MODE_FILTER , SECCOMP_FILTER_FLAG_TSYNC , pointer ) != 0 ) {
346
+ method = 0 ;
278
347
int errno1 = Native .getLastError ();
279
348
if (logger .isDebugEnabled ()) {
280
349
logger .debug ("seccomp(SECCOMP_SET_MODE_FILTER): " + JNACLibrary .strerror (errno1 ) + ", falling back to prctl(PR_SET_SECCOMP)..." );
281
350
}
282
- if (linux_libc .prctl (PR_SET_SECCOMP , SECCOMP_MODE_FILTER , pointer , 0 , 0 ) < 0 ) {
351
+ if (linux_libc .prctl (PR_SET_SECCOMP , SECCOMP_MODE_FILTER , pointer , 0 , 0 ) != 0 ) {
283
352
int errno2 = Native .getLastError ();
284
353
throw new UnsupportedOperationException ("seccomp(SECCOMP_SET_MODE_FILTER): " + JNACLibrary .strerror (errno1 ) +
285
354
", prctl(PR_SET_SECCOMP): " + JNACLibrary .strerror (errno2 ));
@@ -291,7 +360,8 @@ private static void linuxImpl() {
291
360
throw new UnsupportedOperationException ("seccomp filter installation did not really succeed. seccomp(PR_GET_SECCOMP): " + JNACLibrary .strerror (Native .getLastError ()));
292
361
}
293
362
294
- logger .debug ("Linux seccomp filter installation successful" );
363
+ logger .debug ("Linux seccomp filter installation successful, threads: [{}]" , method == 1 ? "all" : "app" );
364
+ return method ;
295
365
}
296
366
297
367
// OS X implementation via sandbox(7)
@@ -334,7 +404,7 @@ private static void macImpl(Path tmpFile) throws IOException {
334
404
// first be defensive: we can give nice errors this way, at the very least.
335
405
boolean supported = Constants .MAC_OS_X ;
336
406
if (supported == false ) {
337
- throw new IllegalStateException ("bug: should not be trying to initialize seccomp for an unsupported OS" );
407
+ throw new IllegalStateException ("bug: should not be trying to initialize seatbelt for an unsupported OS" );
338
408
}
339
409
340
410
// we couldn't link methods, could be some really ancient OS X (< Leopard) or some bug
@@ -372,12 +442,14 @@ private static void macImpl(Path tmpFile) throws IOException {
372
442
* Attempt to drop the capability to execute for the process.
373
443
* <p>
374
444
* This is best effort and OS and architecture dependent. It may throw any Throwable.
445
+ * @return 0 if we can do this for application threads, 1 for the entire process
375
446
*/
376
- static void init (Path tmpFile ) throws Throwable {
447
+ static int init (Path tmpFile ) throws Throwable {
377
448
if (Constants .LINUX ) {
378
- linuxImpl ();
449
+ return linuxImpl ();
379
450
} else if (Constants .MAC_OS_X ) {
380
451
macImpl (tmpFile );
452
+ return 1 ;
381
453
} else {
382
454
throw new UnsupportedOperationException ("syscall filtering not supported for OS: '" + Constants .OS_NAME + "'" );
383
455
}
0 commit comments