diff options
author | Phaedrus Leeds <mwleeds@protonmail.com> | 2021-10-26 12:19:55 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-26 12:19:55 -0700 |
commit | d870def5aaa60a988a74c818fa142c70529218e2 (patch) | |
tree | f4cfe7a0ed3646c0cb6d42460108cfaa90ebe5b3 | |
parent | 2181f4f171abf824d8e3c754e9e7415813460856 (diff) | |
parent | 7c5aec474caef7aa004286cc9359611ad21d227b (diff) | |
download | flatpak-d870def5aaa60a988a74c818fa142c70529218e2.tar.gz |
Merge pull request #4505 from smcv/seccomp-test-coverage
Add test coverage for our seccomp filter
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile-test-matrix.am.inc | 1 | ||||
-rw-r--r-- | tests/Makefile.am.inc | 6 | ||||
-rwxr-xr-x | tests/test-seccomp.sh | 94 | ||||
-rw-r--r-- | tests/try-syscall.c | 173 |
5 files changed, 275 insertions, 0 deletions
@@ -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; +} |