summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhaedrus Leeds <mwleeds@protonmail.com>2021-10-26 12:19:55 -0700
committerGitHub <noreply@github.com>2021-10-26 12:19:55 -0700
commitd870def5aaa60a988a74c818fa142c70529218e2 (patch)
treef4cfe7a0ed3646c0cb6d42460108cfaa90ebe5b3
parent2181f4f171abf824d8e3c754e9e7415813460856 (diff)
parent7c5aec474caef7aa004286cc9359611ad21d227b (diff)
downloadflatpak-d870def5aaa60a988a74c818fa142c70529218e2.tar.gz
Merge pull request #4505 from smcv/seccomp-test-coverage
Add test coverage for our seccomp filter
-rw-r--r--.gitignore1
-rw-r--r--tests/Makefile-test-matrix.am.inc1
-rw-r--r--tests/Makefile.am.inc6
-rwxr-xr-xtests/test-seccomp.sh94
-rw-r--r--tests/try-syscall.c173
5 files changed, 275 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index f02a46b5..c69f817c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -134,6 +134,7 @@ Flatpak-1.0.*
/tests/hold-lock
/tests/list-unused
/tests/mock-flatpak
+/tests/try-syscall
*.test
/tests/*.sh.log
/tests/*.sh.test
diff --git a/tests/Makefile-test-matrix.am.inc b/tests/Makefile-test-matrix.am.inc
index df6493c8..7f302ca9 100644
--- a/tests/Makefile-test-matrix.am.inc
+++ b/tests/Makefile-test-matrix.am.inc
@@ -42,6 +42,7 @@ TEST_MATRIX_DIST= \
tests/test-auth.sh \
tests/test-unused.sh \
tests/test-prune.sh \
+ tests/test-seccomp.sh \
$(NULL)
TEST_MATRIX_EXTRA_DIST= \
tests/test-run.sh \
diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc
index bb0d5a24..e97c23fb 100644
--- a/tests/Makefile.am.inc
+++ b/tests/Makefile.am.inc
@@ -143,6 +143,10 @@ tests_test_authenticator_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) \
tests_test_authenticator_LDADD = $(AM_LDADD) $(BASE_LIBS) libflatpak-common.la libflatpak-common-base.la libglnx.la
tests_test_authenticator_SOURCES = tests/test-authenticator.c
+tests_try_syscall_CFLAGS = $(AM_CFLAGS)
+tests_try_syscall_LDADD = $(AM_LDADD)
+tests_try_syscall_SOURCES = tests/try-syscall.c
+
tests_list_unused_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(SOUP_CFLAGS) $(JSON_CFLAGS) $(APPSTREAM_GLIB_CFLAGS) \
-DFLATPAK_COMPILATION \
-DLOCALEDIR=\"$(localedir)\"
@@ -276,6 +280,7 @@ TEST_MATRIX_SOURCE = \
tests/test-summaries.sh{user+system} \
tests/test-subset.sh{user+system} \
tests/test-prune.sh \
+ tests/test-seccomp.sh \
$(NULL)
update-test-matrix:
@@ -317,6 +322,7 @@ test_extra_programs = \
tests/test-authenticator \
tests/test-portal-impl \
tests/test-update-portal \
+ tests/try-syscall \
$(NULL)
@VALGRIND_CHECK_RULES@
diff --git a/tests/test-seccomp.sh b/tests/test-seccomp.sh
new file mode 100755
index 00000000..72b0dad2
--- /dev/null
+++ b/tests/test-seccomp.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+# Copyright 2021 Collabora Ltd.
+# SPDX-License-Identifier: LGPL-2.0-or-later
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+skip_without_bwrap
+
+echo "1..16"
+
+setup_repo
+install_repo
+
+cp -a "$G_TEST_BUILDDIR/try-syscall" "$test_tmpdir/try-syscall"
+
+# How this works:
+# try-syscall tries to make various syscalls, some benign, some not.
+#
+# The parameters are chosen to make them fail with EBADF or EFAULT if
+# not blocked. If they are blocked, we get ENOSYS or EPERM. If the syscall
+# is impossible for a particular architecture, we get ENOENT.
+#
+# The exit status is an errno value, which we can compare with the expected
+# errno value.
+
+eval "$("$test_tmpdir/try-syscall" print-errno-values)"
+
+try_syscall () {
+ ${FLATPAK} run \
+ --filesystem="$test_tmpdir" \
+ --command="$test_tmpdir/try-syscall" \
+ $extra_argv \
+ org.test.Hello "$@"
+}
+
+for extra_argv in "" "--allow=multiarch"; do
+ echo "# testing with extra argv: '$extra_argv'"
+
+ echo "# chmod (benign)"
+ e=0
+ try_syscall chmod || e="$?"
+ assert_streq "$e" "$EFAULT"
+ ok "chmod not blocked"
+
+ echo "# chroot (harmful)"
+ e=0
+ try_syscall chroot || e="$?"
+ assert_streq "$e" "$EPERM"
+ ok "chroot blocked with EPERM"
+
+ echo "# clone3 (harmful)"
+ e=0
+ try_syscall clone3 || e="$?"
+ # This is either ENOSYS because the kernel genuinely doesn't implement it,
+ # or because we successfully blocked it. We can't tell which.
+ assert_streq "$e" "$ENOSYS"
+ ok "clone3 blocked with ENOSYS (CVE-2021-41133)"
+
+ echo "# ioctl TIOCNOTTY (benign)"
+ e=0
+ try_syscall "ioctl TIOCNOTTY" || e="$?"
+ assert_streq "$e" "$EBADF"
+ ok "ioctl TIOCNOTTY not blocked"
+
+ echo "# ioctl TIOCSTI (CVE-2017-5226)"
+ e=0
+ try_syscall "ioctl TIOCSTI" || e="$?"
+ assert_streq "$e" "$EPERM"
+ ok "ioctl TIOCSTI blocked (CVE-2017-5226)"
+
+ echo "# ioctl TIOCSTI (trying to repeat CVE-2019-10063)"
+ e=0
+ try_syscall "ioctl TIOCSTI CVE-2019-10063" || e="$?"
+ if test "$e" = "$ENOENT"; then
+ echo "ok # SKIP Cannot replicate CVE-2019-10063 on 32-bit architecture"
+ else
+ assert_streq "$e" "$EPERM"
+ ok "ioctl TIOCSTI with high bits blocked (CVE-2019-10063)"
+ fi
+
+ echo "# listen (benign)"
+ e=0
+ try_syscall "listen" || e="$?"
+ assert_streq "$e" "$EBADF"
+ ok "listen not blocked"
+
+ echo "# prctl (benign)"
+ e=0
+ try_syscall "prctl" || e="$?"
+ assert_streq "$e" "$EFAULT"
+ ok "prctl not blocked"
+done
diff --git a/tests/try-syscall.c b/tests/try-syscall.c
new file mode 100644
index 00000000..84a0ca66
--- /dev/null
+++ b/tests/try-syscall.c
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#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;
+}