mirror of
https://github.com/flatpak/flatpak.git
synced 2026-05-11 17:36:44 -04:00
Merge pull request #4505 from smcv/seccomp-test-coverage
Add test coverage for our seccomp filter
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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@
|
||||
|
||||
94
tests/test-seccomp.sh
Executable file
94
tests/test-seccomp.sh
Executable file
@@ -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
|
||||
173
tests/try-syscall.c
Normal file
173
tests/try-syscall.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user