summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <smcv@collabora.com>2021-10-12 00:56:20 +0100
committerSimon McVittie <smcv@collabora.com>2022-01-31 17:13:46 +0000
commit3612534c2cce3c3b32a59f57b4a68502b059cce7 (patch)
tree3192718485d43e4dd1cf81b033eb14b0298eb8f8
parentd625fda8da406cedcad2dcdd02af203d90543fae (diff)
downloadbubblewrap-3612534c2cce3c3b32a59f57b4a68502b059cce7.tar.gz
tests: Exercise seccomp filters
Signed-off-by: Simon McVittie <smcv@collabora.com>
-rw-r--r--Makefile.am2
-rwxr-xr-xtests/test-seccomp.py635
-rw-r--r--tests/try-syscall.c176
3 files changed, 813 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 59051e5..9c8145e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,11 +32,13 @@ test_programs = \
$(NULL)
test_scripts = \
tests/test-run.sh \
+ tests/test-seccomp.py \
tests/test-specifying-userns.sh \
tests/test-specifying-pidns.sh \
$(NULL)
test_extra_programs = \
test-bwrap \
+ tests/try-syscall \
$(NULL)
test-bwrap: bwrap
diff --git a/tests/test-seccomp.py b/tests/test-seccomp.py
new file mode 100755
index 0000000..3ce02ef
--- /dev/null
+++ b/tests/test-seccomp.py
@@ -0,0 +1,635 @@
+#!/usr/bin/env python3
+# Copyright 2021 Simon McVittie
+# SPDX-License-Identifier: LGPL-2.0-or-later
+
+import errno
+import logging
+import os
+import subprocess
+import sys
+import tempfile
+import termios
+import unittest
+
+try:
+ import seccomp
+except ImportError:
+ print('1..0 # SKIP cannot import seccomp Python module')
+ sys.exit(0)
+
+
+# This is the @default set from systemd as of 2021-10-11
+DEFAULT_SET = set('''
+brk
+cacheflush
+clock_getres
+clock_getres_time64
+clock_gettime
+clock_gettime64
+clock_nanosleep
+clock_nanosleep_time64
+execve
+exit
+exit_group
+futex
+futex_time64
+get_robust_list
+get_thread_area
+getegid
+getegid32
+geteuid
+geteuid32
+getgid
+getgid32
+getgroups
+getgroups32
+getpgid
+getpgrp
+getpid
+getppid
+getrandom
+getresgid
+getresgid32
+getresuid
+getresuid32
+getrlimit
+getsid
+gettid
+gettimeofday
+getuid
+getuid32
+membarrier
+mmap
+mmap2
+munmap
+nanosleep
+pause
+prlimit64
+restart_syscall
+rseq
+rt_sigreturn
+sched_getaffinity
+sched_yield
+set_robust_list
+set_thread_area
+set_tid_address
+set_tls
+sigreturn
+time
+ugetrlimit
+'''.split())
+
+# This is the @basic-io set from systemd
+BASIC_IO_SET = set('''
+_llseek
+close
+close_range
+dup
+dup2
+dup3
+lseek
+pread64
+preadv
+preadv2
+pwrite64
+pwritev
+pwritev2
+read
+readv
+write
+writev
+'''.split())
+
+# This is the @filesystem-io set from systemd
+FILESYSTEM_SET = set('''
+access
+chdir
+chmod
+close
+creat
+faccessat
+faccessat2
+fallocate
+fchdir
+fchmod
+fchmodat
+fcntl
+fcntl64
+fgetxattr
+flistxattr
+fremovexattr
+fsetxattr
+fstat
+fstat64
+fstatat64
+fstatfs
+fstatfs64
+ftruncate
+ftruncate64
+futimesat
+getcwd
+getdents
+getdents64
+getxattr
+inotify_add_watch
+inotify_init
+inotify_init1
+inotify_rm_watch
+lgetxattr
+link
+linkat
+listxattr
+llistxattr
+lremovexattr
+lsetxattr
+lstat
+lstat64
+mkdir
+mkdirat
+mknod
+mknodat
+newfstatat
+oldfstat
+oldlstat
+oldstat
+open
+openat
+openat2
+readlink
+readlinkat
+removexattr
+rename
+renameat
+renameat2
+rmdir
+setxattr
+stat
+stat64
+statfs
+statfs64
+statx
+symlink
+symlinkat
+truncate
+truncate64
+unlink
+unlinkat
+utime
+utimensat
+utimensat_time64
+utimes
+'''.split())
+
+# Miscellaneous syscalls used during process startup, at least on x86_64
+ALLOWED = DEFAULT_SET | BASIC_IO_SET | FILESYSTEM_SET | set('''
+arch_prctl
+ioctl
+madvise
+mprotect
+mremap
+prctl
+readdir
+umask
+'''.split())
+
+# Syscalls we will try to use, expecting them to be either allowed or
+# blocked by our allow and/or deny lists
+TRY_SYSCALLS = [
+ 'chmod',
+ 'chroot',
+ 'clone3',
+ 'ioctl TIOCNOTTY',
+ 'ioctl TIOCSTI CVE-2019-10063',
+ 'ioctl TIOCSTI',
+ 'listen',
+ 'prctl',
+]
+
+
+class Test(unittest.TestCase):
+ def setUp(self) -> None:
+ here = os.path.dirname(os.path.abspath(__file__))
+
+ if 'G_TEST_SRCDIR' in os.environ:
+ self.test_srcdir = os.getenv('G_TEST_SRCDIR') + '/tests'
+ else:
+ self.test_srcdir = here
+
+ if 'G_TEST_BUILDDIR' in os.environ:
+ self.test_builddir = os.getenv('G_TEST_BUILDDIR') + '/tests'
+ else:
+ self.test_builddir = here
+
+ self.bwrap = os.getenv('BWRAP', 'bwrap')
+ self.try_syscall = os.path.join(self.test_builddir, 'try-syscall')
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ 'true',
+ ],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=2,
+ )
+
+ if completed.returncode != 0:
+ raise unittest.SkipTest(
+ 'cannot run bwrap (does it need to be setuid?)'
+ )
+
+ def tearDown(self) -> None:
+ pass
+
+ def test_no_seccomp(self) -> None:
+ for syscall in TRY_SYSCALLS:
+ print('# {} without seccomp'.format(syscall))
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ self.try_syscall, syscall,
+ ],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=2,
+ )
+
+ if (
+ syscall == 'ioctl TIOCSTI CVE-2019-10063'
+ and completed.returncode == errno.ENOENT
+ ):
+ print('# Cannot test 64-bit syscall parameter on 32-bit')
+ continue
+
+ if syscall == 'clone3':
+ # If the kernel supports it, we didn't block it so
+ # it fails with EFAULT. If the kernel doesn't support it,
+ # it'll fail with ENOSYS instead.
+ self.assertIn(
+ completed.returncode,
+ (errno.ENOSYS, errno.EFAULT),
+ )
+ elif syscall.startswith('ioctl') or syscall == 'listen':
+ self.assertEqual(completed.returncode, errno.EBADF)
+ else:
+ self.assertEqual(completed.returncode, errno.EFAULT)
+
+ def test_seccomp_allowlist(self) -> None:
+ with tempfile.TemporaryFile() as allowlist_temp:
+ allowlist = seccomp.SyscallFilter(seccomp.ERRNO(errno.ENOSYS))
+
+ if os.uname().machine == 'x86_64':
+ # Allow Python and try-syscall to be different word sizes
+ allowlist.add_arch(seccomp.Arch.X86)
+
+ for syscall in ALLOWED:
+ try:
+ allowlist.add_rule(seccomp.ALLOW, syscall)
+ except Exception as e:
+ print('# Cannot add {} to allowlist: {!r}'.format(syscall, e))
+
+ allowlist.export_bpf(allowlist_temp)
+
+ for syscall in TRY_SYSCALLS:
+ print('# allowlist vs. {}'.format(syscall))
+ allowlist_temp.seek(0, os.SEEK_SET)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--seccomp', str(allowlist_temp.fileno()),
+ self.try_syscall, syscall,
+ ],
+ pass_fds=(allowlist_temp.fileno(),),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=2,
+ )
+
+ if (
+ syscall == 'ioctl TIOCSTI CVE-2019-10063'
+ and completed.returncode == errno.ENOENT
+ ):
+ print('# Cannot test 64-bit syscall parameter on 32-bit')
+ continue
+
+ if syscall.startswith('ioctl'):
+ # We allow this, so it is executed (and in this simple
+ # example, immediately fails)
+ self.assertEqual(completed.returncode, errno.EBADF)
+ elif syscall in ('chroot', 'listen', 'clone3'):
+ # We don't allow these, so they fail with ENOSYS.
+ # clone3 might also be failing with ENOSYS because
+ # the kernel genuinely doesn't support it.
+ self.assertEqual(completed.returncode, errno.ENOSYS)
+ else:
+ # We allow this, so it is executed (and in this simple
+ # example, immediately fails)
+ self.assertEqual(completed.returncode, errno.EFAULT)
+
+ def test_seccomp_denylist(self) -> None:
+ with tempfile.TemporaryFile() as denylist_temp:
+ denylist = seccomp.SyscallFilter(seccomp.ALLOW)
+
+ if os.uname().machine == 'x86_64':
+ # Allow Python and try-syscall to be different word sizes
+ denylist.add_arch(seccomp.Arch.X86)
+
+ # Using ECONNREFUSED here because it's unlikely that any of
+ # these syscalls will legitimately fail with that code, so
+ # if they fail like this, it will be as a result of seccomp.
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chmod')
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chroot')
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'prctl')
+ denylist.add_rule(
+ seccomp.ERRNO(errno.ECONNREFUSED), 'ioctl',
+ seccomp.Arg(1, seccomp.MASKED_EQ, 0xffffffff, termios.TIOCSTI),
+ )
+
+ denylist.export_bpf(denylist_temp)
+
+ for syscall in TRY_SYSCALLS:
+ print('# denylist vs. {}'.format(syscall))
+ denylist_temp.seek(0, os.SEEK_SET)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--seccomp', str(denylist_temp.fileno()),
+ self.try_syscall, syscall,
+ ],
+ pass_fds=(denylist_temp.fileno(),),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=2,
+ )
+
+ if (
+ syscall == 'ioctl TIOCSTI CVE-2019-10063'
+ and completed.returncode == errno.ENOENT
+ ):
+ print('# Cannot test 64-bit syscall parameter on 32-bit')
+ continue
+
+ if syscall == 'clone3':
+ # If the kernel supports it, we didn't block it so
+ # it fails with EFAULT. If the kernel doesn't support it,
+ # it'll fail with ENOSYS instead.
+ self.assertIn(
+ completed.returncode,
+ (errno.ENOSYS, errno.EFAULT),
+ )
+ elif syscall in ('ioctl TIOCNOTTY', 'listen'):
+ # Not on the denylist
+ self.assertEqual(completed.returncode, errno.EBADF)
+ else:
+ # We blocked all of these
+ self.assertEqual(completed.returncode, errno.ECONNREFUSED)
+
+ def test_seccomp_stacked(self, allowlist_first=False) -> None:
+ with tempfile.TemporaryFile(
+ ) as allowlist_temp, tempfile.TemporaryFile(
+ ) as denylist_temp:
+ # This filter is a simplified version of what Flatpak wants
+
+ allowlist = seccomp.SyscallFilter(seccomp.ERRNO(errno.ENOSYS))
+ denylist = seccomp.SyscallFilter(seccomp.ALLOW)
+
+ if os.uname().machine == 'x86_64':
+ # Allow Python and try-syscall to be different word sizes
+ allowlist.add_arch(seccomp.Arch.X86)
+ denylist.add_arch(seccomp.Arch.X86)
+
+ for syscall in ALLOWED:
+ try:
+ allowlist.add_rule(seccomp.ALLOW, syscall)
+ except Exception as e:
+ print('# Cannot add {} to allowlist: {!r}'.format(syscall, e))
+
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chmod')
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'chroot')
+ denylist.add_rule(
+ seccomp.ERRNO(errno.ECONNREFUSED), 'ioctl',
+ seccomp.Arg(1, seccomp.MASKED_EQ, 0xffffffff, termios.TIOCSTI),
+ )
+
+ # All seccomp programs except the last must allow prctl(),
+ # because otherwise we wouldn't be able to add the remaining
+ # seccomp programs. We document that the last program can
+ # block prctl, so test that.
+ if allowlist_first:
+ denylist.add_rule(seccomp.ERRNO(errno.ECONNREFUSED), 'prctl')
+
+ allowlist.export_bpf(allowlist_temp)
+ denylist.export_bpf(denylist_temp)
+
+ for syscall in TRY_SYSCALLS:
+ print('# stacked vs. {}'.format(syscall))
+ allowlist_temp.seek(0, os.SEEK_SET)
+ denylist_temp.seek(0, os.SEEK_SET)
+
+ if allowlist_first:
+ fds = [allowlist_temp.fileno(), denylist_temp.fileno()]
+ else:
+ fds = [denylist_temp.fileno(), allowlist_temp.fileno()]
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--add-seccomp-fd', str(fds[0]),
+ '--add-seccomp-fd', str(fds[1]),
+ self.try_syscall, syscall,
+ ],
+ pass_fds=fds,
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=2,
+ )
+
+ if (
+ syscall == 'ioctl TIOCSTI CVE-2019-10063'
+ and completed.returncode == errno.ENOENT
+ ):
+ print('# Cannot test 64-bit syscall parameter on 32-bit')
+ continue
+
+ if syscall == 'ioctl TIOCNOTTY':
+ # Not denied by the denylist, and allowed by the allowlist
+ self.assertEqual(completed.returncode, errno.EBADF)
+ elif syscall in ('clone3', 'listen'):
+ # We didn't deny these, so the denylist has no effect
+ # and we fall back to the allowlist, which doesn't
+ # include them either.
+ # clone3 might also be failing with ENOSYS because
+ # the kernel genuinely doesn't support it.
+ self.assertEqual(completed.returncode, errno.ENOSYS)
+ elif syscall == 'chroot':
+ # This is denied by the denylist *and* not allowed by
+ # the allowlist. The result depends which one we added
+ # first: the most-recently-added filter "wins".
+ if allowlist_first:
+ self.assertEqual(
+ completed.returncode,
+ errno.ECONNREFUSED,
+ )
+ else:
+ self.assertEqual(completed.returncode, errno.ENOSYS)
+ elif syscall == 'prctl':
+ # We can only put this on the denylist if the denylist
+ # is the last to be added.
+ if allowlist_first:
+ self.assertEqual(
+ completed.returncode,
+ errno.ECONNREFUSED,
+ )
+ else:
+ self.assertEqual(completed.returncode, errno.EFAULT)
+ else:
+ # chmod is allowed by the allowlist but blocked by the
+ # denylist. Denying takes precedence over allowing,
+ # regardless of order.
+ self.assertEqual(completed.returncode, errno.ECONNREFUSED)
+
+ def test_seccomp_stacked_allowlist_first(self) -> None:
+ self.test_seccomp_stacked(allowlist_first=True)
+
+ def test_seccomp_invalid(self) -> None:
+ with tempfile.TemporaryFile(
+ ) as allowlist_temp, tempfile.TemporaryFile(
+ ) as denylist_temp:
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--add-seccomp-fd', '-1',
+ 'true',
+ ],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(b'bwrap: Invalid fd: -1\n', completed.stderr)
+ self.assertEqual(completed.returncode, 1)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--seccomp', '0a',
+ 'true',
+ ],
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(b'bwrap: Invalid fd: 0a\n', completed.stderr)
+ self.assertEqual(completed.returncode, 1)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--add-seccomp-fd', str(denylist_temp.fileno()),
+ '--seccomp', str(allowlist_temp.fileno()),
+ 'true',
+ ],
+ pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(
+ b'bwrap: --seccomp cannot be combined with --add-seccomp-fd\n',
+ completed.stderr,
+ )
+ self.assertEqual(completed.returncode, 1)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--seccomp', str(allowlist_temp.fileno()),
+ '--add-seccomp-fd', str(denylist_temp.fileno()),
+ 'true',
+ ],
+ pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(
+ b'--add-seccomp-fd cannot be combined with --seccomp',
+ completed.stderr,
+ )
+ self.assertEqual(completed.returncode, 1)
+
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--add-seccomp-fd', str(allowlist_temp.fileno()),
+ '--add-seccomp-fd', str(allowlist_temp.fileno()),
+ 'true',
+ ],
+ pass_fds=(allowlist_temp.fileno(), allowlist_temp.fileno()),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(
+ b"bwrap: Can't read seccomp data: ",
+ completed.stderr,
+ )
+ self.assertEqual(completed.returncode, 1)
+
+ allowlist_temp.write(b'\x01')
+ allowlist_temp.seek(0, os.SEEK_SET)
+ completed = subprocess.run(
+ [
+ self.bwrap,
+ '--ro-bind', '/', '/',
+ '--add-seccomp-fd', str(denylist_temp.fileno()),
+ '--add-seccomp-fd', str(allowlist_temp.fileno()),
+ 'true',
+ ],
+ pass_fds=(allowlist_temp.fileno(), denylist_temp.fileno()),
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE,
+ )
+ self.assertIn(
+ b'bwrap: Invalid seccomp data, must be multiple of 8\n',
+ completed.stderr,
+ )
+ self.assertEqual(completed.returncode, 1)
+
+
+def main():
+ logging.basicConfig(level=logging.DEBUG)
+
+ try:
+ from tap.runner import TAPTestRunner
+ except ImportError:
+ TAPTestRunner = None # type: ignore
+
+ if TAPTestRunner is not None:
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
+ else:
+ print('# tap.runner not available, using simple TAP output')
+ print('1..1')
+ program = unittest.main(exit=False)
+ if program.result.wasSuccessful():
+ print('ok 1 - %r' % program.result)
+ else:
+ print('not ok 1 - %r' % program.result)
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/try-syscall.c b/tests/try-syscall.c
new file mode 100644
index 0000000..df35054
--- /dev/null
+++ b/tests/try-syscall.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2021 Simon McVittie
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ *
+ * Try one or more system calls that might have been blocked by a
+ * seccomp filter. Return the last value of errno seen.
+ *
+ * In general, we pass a bad fd or pointer to each syscall that will
+ * accept one, so that it will fail with EBADF or EFAULT without side-effects.
+ *
+ * This helper is used for regression tests in both bubblewrap and flatpak.
+ * Please keep both copies in sync.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined(_MIPS_SIM)
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define MISSING_SYSCALL_BASE 4000
+# elif _MIPS_SIM == _MIPS_SIM_ABI64
+# define MISSING_SYSCALL_BASE 5000
+# elif _MIPS_SIM == _MIPS_SIM_NABI32
+# define MISSING_SYSCALL_BASE 6000
+# else
+# error "Unknown MIPS ABI"
+# endif
+#endif
+
+#if defined(__ia64__)
+# define MISSING_SYSCALL_BASE 1024
+#endif
+
+#if defined(__alpha__)
+# define MISSING_SYSCALL_BASE 110
+#endif
+
+#if defined(__x86_64__) && defined(__ILP32__)
+# define MISSING_SYSCALL_BASE 0x40000000
+#endif
+
+/*
+ * MISSING_SYSCALL_BASE:
+ *
+ * Number to add to the syscall numbers of recently-added syscalls
+ * to get the appropriate syscall for the current ABI.
+ */
+#ifndef MISSING_SYSCALL_BASE
+# define MISSING_SYSCALL_BASE 0
+#endif
+
+#ifndef __NR_clone3
+# define __NR_clone3 (MISSING_SYSCALL_BASE + 435)
+#endif
+
+/*
+ * The size of clone3's parameter (as of 2021)
+ */
+#define SIZEOF_STRUCT_CLONE_ARGS ((size_t) 88)
+
+/*
+ * An invalid pointer that will cause syscalls to fail with EFAULT
+ */
+#define WRONG_POINTER ((char *) 1)
+
+int
+main (int argc, char **argv)
+{
+ int errsv = 0;
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char *arg = argv[i];
+
+ if (strcmp (arg, "print-errno-values") == 0)
+ {
+ printf ("EBADF=%d\n", EBADF);
+ printf ("EFAULT=%d\n", EFAULT);
+ printf ("ENOENT=%d\n", ENOENT);
+ printf ("ENOSYS=%d\n", ENOSYS);
+ printf ("EPERM=%d\n", EPERM);
+ }
+ else if (strcmp (arg, "chmod") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EFAULT */
+ if (chmod (WRONG_POINTER, 0700) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else if (strcmp (arg, "chroot") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EFAULT */
+ if (chroot (WRONG_POINTER) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else if (strcmp (arg, "clone3") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EFAULT */
+ if (syscall (__NR_clone3, WRONG_POINTER, SIZEOF_STRUCT_CLONE_ARGS) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else if (strcmp (arg, "ioctl TIOCNOTTY") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EBADF */
+ if (ioctl (-1, TIOCNOTTY) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else if (strcmp (arg, "ioctl TIOCSTI") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EBADF */
+ if (ioctl (-1, TIOCSTI, WRONG_POINTER) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+#ifdef __LP64__
+ else if (strcmp (arg, "ioctl TIOCSTI CVE-2019-10063") == 0)
+ {
+ unsigned long not_TIOCSTI = (0x123UL << 32) | (unsigned long) TIOCSTI;
+
+ /* If not blocked by seccomp, this will fail with EBADF */
+ if (syscall (__NR_ioctl, -1, not_TIOCSTI, WRONG_POINTER) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+#endif
+ else if (strcmp (arg, "listen") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EBADF */
+ if (listen (-1, 42) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else if (strcmp (arg, "prctl") == 0)
+ {
+ /* If not blocked by seccomp, this will fail with EFAULT */
+ if (prctl (PR_GET_CHILD_SUBREAPER, WRONG_POINTER, 0, 0, 0) != 0)
+ {
+ errsv = errno;
+ perror (arg);
+ }
+ }
+ else
+ {
+ fprintf (stderr, "Unsupported syscall \"%s\"\n", arg);
+ errsv = ENOENT;
+ }
+ }
+
+ return errsv;
+}