Major additions:
- sysctl and seccomp operation to discover available actions. (tyhicks) - new per-filter configurable logging infrastructure and sysctl. (tyhicks) - SECCOMP_RET_LOG to log allowed syscalls. (tyhicks) - SECCOMP_RET_KILL_PROCESS as the new strictest possible action. - self-tests for new behaviors. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 Comment: Kees Cook <kees@outflux.net> iQIcBAABCgAGBQJZxVbTAAoJEIly9N/cbcAmvIAQALR9aVQQXjma4lLhZxwTsLtG rJm8t/o4y/2aBV8vzpFbMPT5gfN/PAkHJpCoxVPssx0k4PH2M7HjpnR6E1OC+erg RNom3uNdNqZeFlDpdX1qriYiCTB9p6rHe0DPwgG9iGqgDxsJ+G3W+x1sMZ1C+A0M shxA3fwt+Qpivo8Zq44xjMFjK+Zeor9V3yPc51QoZktWHlM16ID3HvHVnUtzqAUb nTWF6ZlmZlJ/lp4Dq8/55lytVcXPo240G3H0Odai+SNFakK6p5UO//BRBV209bmb 05jpAOH6uym1sxVz00TQXCtDqOEzs2mQgomtTSShHg8SrLFX7nFkEFtAVA6tEri2 FqDYce9KX7ZtOYiq83C7pnpAFCouc0z31dQl9USHiAiexXklwBIX+OsVv98omWGi pW43uLE2ovY0cpOsN50xI4mnxiGh6MhFcdbor2VLRJwLIFSw3XjjgNCCLyK4AJxs N514252qi70c9cWyAHYDLy077yTVxu3JUlsVQKtRTMfoFUq6bX1jPXVXE8qkVrui bc/Ay54pPrUwM854IpQ9ZBOuMfs6I5opocGIsBvMaND45U4o2B0ANCsxhuZ0zEtM E55DhK5OgjukNemQmlWK2foDckYdtkJXCj2yMBNQady0Uynr2BWZ6VDBP7vFcnRB UihRlFZRZleu8383uHsc =sKeC -----END PGP SIGNATURE----- Merge tag 'seccomp-v4.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull seccomp updates from Kees Cook: "Major additions: - sysctl and seccomp operation to discover available actions (tyhicks) - new per-filter configurable logging infrastructure and sysctl (tyhicks) - SECCOMP_RET_LOG to log allowed syscalls (tyhicks) - SECCOMP_RET_KILL_PROCESS as the new strictest possible action - self-tests for new behaviors" [ This is the seccomp part of the security pull request during the merge window that was nixed due to unrelated problems - Linus ] * tag 'seccomp-v4.14-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: samples: Unrename SECCOMP_RET_KILL selftests/seccomp: Test thread vs process killing seccomp: Implement SECCOMP_RET_KILL_PROCESS action seccomp: Introduce SECCOMP_RET_KILL_PROCESS seccomp: Rename SECCOMP_RET_KILL to SECCOMP_RET_KILL_THREAD seccomp: Action to log before allowing seccomp: Filter flag to log all actions except SECCOMP_RET_ALLOW seccomp: Selftest for detection of filter flag support seccomp: Sysctl to configure actions that are allowed to be logged seccomp: Operation for checking if an action is available seccomp: Sysctl to display available actions seccomp: Provide matching filter for introspection selftests/seccomp: Refactor RET_ERRNO tests selftests/seccomp: Add simple seccomp overhead benchmark selftests/seccomp: Add tests for basic ptrace actionszero-colors
commit
c0a3a64e72
|
@ -337,7 +337,7 @@ Examples for low-level BPF:
|
||||||
jeq #14, good /* __NR_rt_sigprocmask */
|
jeq #14, good /* __NR_rt_sigprocmask */
|
||||||
jeq #13, good /* __NR_rt_sigaction */
|
jeq #13, good /* __NR_rt_sigaction */
|
||||||
jeq #35, good /* __NR_nanosleep */
|
jeq #35, good /* __NR_nanosleep */
|
||||||
bad: ret #0 /* SECCOMP_RET_KILL */
|
bad: ret #0 /* SECCOMP_RET_KILL_THREAD */
|
||||||
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
|
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
|
||||||
|
|
||||||
The above example code can be placed into a file (here called "foo"), and
|
The above example code can be placed into a file (here called "foo"), and
|
||||||
|
|
|
@ -75,6 +75,7 @@ show up in /proc/sys/kernel:
|
||||||
- reboot-cmd [ SPARC only ]
|
- reboot-cmd [ SPARC only ]
|
||||||
- rtsig-max
|
- rtsig-max
|
||||||
- rtsig-nr
|
- rtsig-nr
|
||||||
|
- seccomp/ ==> Documentation/userspace-api/seccomp_filter.rst
|
||||||
- sem
|
- sem
|
||||||
- sem_next_id [ sysv ipc ]
|
- sem_next_id [ sysv ipc ]
|
||||||
- sg-big-buff [ generic SCSI device (sg) ]
|
- sg-big-buff [ generic SCSI device (sg) ]
|
||||||
|
|
|
@ -87,11 +87,16 @@ Return values
|
||||||
A seccomp filter may return any of the following values. If multiple
|
A seccomp filter may return any of the following values. If multiple
|
||||||
filters exist, the return value for the evaluation of a given system
|
filters exist, the return value for the evaluation of a given system
|
||||||
call will always use the highest precedent value. (For example,
|
call will always use the highest precedent value. (For example,
|
||||||
``SECCOMP_RET_KILL`` will always take precedence.)
|
``SECCOMP_RET_KILL_PROCESS`` will always take precedence.)
|
||||||
|
|
||||||
In precedence order, they are:
|
In precedence order, they are:
|
||||||
|
|
||||||
``SECCOMP_RET_KILL``:
|
``SECCOMP_RET_KILL_PROCESS``:
|
||||||
|
Results in the entire process exiting immediately without executing
|
||||||
|
the system call. The exit status of the task (``status & 0x7f``)
|
||||||
|
will be ``SIGSYS``, not ``SIGKILL``.
|
||||||
|
|
||||||
|
``SECCOMP_RET_KILL_THREAD``:
|
||||||
Results in the task exiting immediately without executing the
|
Results in the task exiting immediately without executing the
|
||||||
system call. The exit status of the task (``status & 0x7f``) will
|
system call. The exit status of the task (``status & 0x7f``) will
|
||||||
be ``SIGSYS``, not ``SIGKILL``.
|
be ``SIGSYS``, not ``SIGKILL``.
|
||||||
|
@ -141,6 +146,15 @@ In precedence order, they are:
|
||||||
allow use of ptrace, even of other sandboxed processes, without
|
allow use of ptrace, even of other sandboxed processes, without
|
||||||
extreme care; ptracers can use this mechanism to escape.)
|
extreme care; ptracers can use this mechanism to escape.)
|
||||||
|
|
||||||
|
``SECCOMP_RET_LOG``:
|
||||||
|
Results in the system call being executed after it is logged. This
|
||||||
|
should be used by application developers to learn which syscalls their
|
||||||
|
application needs without having to iterate through multiple test and
|
||||||
|
development cycles to build the list.
|
||||||
|
|
||||||
|
This action will only be logged if "log" is present in the
|
||||||
|
actions_logged sysctl string.
|
||||||
|
|
||||||
``SECCOMP_RET_ALLOW``:
|
``SECCOMP_RET_ALLOW``:
|
||||||
Results in the system call being executed.
|
Results in the system call being executed.
|
||||||
|
|
||||||
|
@ -169,7 +183,41 @@ The ``samples/seccomp/`` directory contains both an x86-specific example
|
||||||
and a more generic example of a higher level macro interface for BPF
|
and a more generic example of a higher level macro interface for BPF
|
||||||
program generation.
|
program generation.
|
||||||
|
|
||||||
|
Sysctls
|
||||||
|
=======
|
||||||
|
|
||||||
|
Seccomp's sysctl files can be found in the ``/proc/sys/kernel/seccomp/``
|
||||||
|
directory. Here's a description of each file in that directory:
|
||||||
|
|
||||||
|
``actions_avail``:
|
||||||
|
A read-only ordered list of seccomp return values (refer to the
|
||||||
|
``SECCOMP_RET_*`` macros above) in string form. The ordering, from
|
||||||
|
left-to-right, is the least permissive return value to the most
|
||||||
|
permissive return value.
|
||||||
|
|
||||||
|
The list represents the set of seccomp return values supported
|
||||||
|
by the kernel. A userspace program may use this list to
|
||||||
|
determine if the actions found in the ``seccomp.h``, when the
|
||||||
|
program was built, differs from the set of actions actually
|
||||||
|
supported in the current running kernel.
|
||||||
|
|
||||||
|
``actions_logged``:
|
||||||
|
A read-write ordered list of seccomp return values (refer to the
|
||||||
|
``SECCOMP_RET_*`` macros above) that are allowed to be logged. Writes
|
||||||
|
to the file do not need to be in ordered form but reads from the file
|
||||||
|
will be ordered in the same way as the actions_avail sysctl.
|
||||||
|
|
||||||
|
It is important to note that the value of ``actions_logged`` does not
|
||||||
|
prevent certain actions from being logged when the audit subsystem is
|
||||||
|
configured to audit a task. If the action is not found in
|
||||||
|
``actions_logged`` list, the final decision on whether to audit the
|
||||||
|
action for that task is ultimately left up to the audit subsystem to
|
||||||
|
decide for all seccomp return values other than ``SECCOMP_RET_ALLOW``.
|
||||||
|
|
||||||
|
The ``allow`` string is not accepted in the ``actions_logged`` sysctl
|
||||||
|
as it is not possible to log ``SECCOMP_RET_ALLOW`` actions. Attempting
|
||||||
|
to write ``allow`` to the sysctl will result in an EINVAL being
|
||||||
|
returned.
|
||||||
|
|
||||||
Adding architecture support
|
Adding architecture support
|
||||||
===========================
|
===========================
|
||||||
|
|
|
@ -314,11 +314,7 @@ void audit_core_dumps(long signr);
|
||||||
|
|
||||||
static inline void audit_seccomp(unsigned long syscall, long signr, int code)
|
static inline void audit_seccomp(unsigned long syscall, long signr, int code)
|
||||||
{
|
{
|
||||||
if (!audit_enabled)
|
if (audit_enabled && unlikely(!audit_dummy_context()))
|
||||||
return;
|
|
||||||
|
|
||||||
/* Force a record to be reported if a signal was delivered. */
|
|
||||||
if (signr || unlikely(!audit_dummy_context()))
|
|
||||||
__audit_seccomp(syscall, signr, code);
|
__audit_seccomp(syscall, signr, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include <uapi/linux/seccomp.h>
|
#include <uapi/linux/seccomp.h>
|
||||||
|
|
||||||
#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC)
|
#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC | \
|
||||||
|
SECCOMP_FILTER_FLAG_LOG)
|
||||||
|
|
||||||
#ifdef CONFIG_SECCOMP
|
#ifdef CONFIG_SECCOMP
|
||||||
|
|
||||||
|
|
|
@ -11,27 +11,34 @@
|
||||||
#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */
|
#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */
|
||||||
|
|
||||||
/* Valid operations for seccomp syscall. */
|
/* Valid operations for seccomp syscall. */
|
||||||
#define SECCOMP_SET_MODE_STRICT 0
|
#define SECCOMP_SET_MODE_STRICT 0
|
||||||
#define SECCOMP_SET_MODE_FILTER 1
|
#define SECCOMP_SET_MODE_FILTER 1
|
||||||
|
#define SECCOMP_GET_ACTION_AVAIL 2
|
||||||
|
|
||||||
/* Valid flags for SECCOMP_SET_MODE_FILTER */
|
/* Valid flags for SECCOMP_SET_MODE_FILTER */
|
||||||
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
||||||
|
#define SECCOMP_FILTER_FLAG_LOG 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All BPF programs must return a 32-bit value.
|
* All BPF programs must return a 32-bit value.
|
||||||
* The bottom 16-bits are for optional return data.
|
* The bottom 16-bits are for optional return data.
|
||||||
* The upper 16-bits are ordered from least permissive values to most.
|
* The upper 16-bits are ordered from least permissive values to most,
|
||||||
|
* as a signed value (so 0x8000000 is negative).
|
||||||
*
|
*
|
||||||
* The ordering ensures that a min_t() over composed return values always
|
* The ordering ensures that a min_t() over composed return values always
|
||||||
* selects the least permissive choice.
|
* selects the least permissive choice.
|
||||||
*/
|
*/
|
||||||
#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */
|
#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */
|
||||||
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
|
#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */
|
||||||
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
|
#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD
|
||||||
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
|
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
|
||||||
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
|
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
|
||||||
|
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
|
||||||
|
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
|
||||||
|
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
|
||||||
|
|
||||||
/* Masks for the return value sections. */
|
/* Masks for the return value sections. */
|
||||||
|
#define SECCOMP_RET_ACTION_FULL 0xffff0000U
|
||||||
#define SECCOMP_RET_ACTION 0x7fff0000U
|
#define SECCOMP_RET_ACTION 0x7fff0000U
|
||||||
#define SECCOMP_RET_DATA 0x0000ffffU
|
#define SECCOMP_RET_DATA 0x0000ffffU
|
||||||
|
|
||||||
|
|
323
kernel/seccomp.c
323
kernel/seccomp.c
|
@ -17,11 +17,13 @@
|
||||||
#include <linux/audit.h>
|
#include <linux/audit.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
#include <linux/coredump.h>
|
#include <linux/coredump.h>
|
||||||
|
#include <linux/kmemleak.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
#include <linux/seccomp.h>
|
#include <linux/seccomp.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/syscalls.h>
|
#include <linux/syscalls.h>
|
||||||
|
#include <linux/sysctl.h>
|
||||||
|
|
||||||
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
|
#ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER
|
||||||
#include <asm/syscall.h>
|
#include <asm/syscall.h>
|
||||||
|
@ -42,6 +44,7 @@
|
||||||
* get/put helpers should be used when accessing an instance
|
* get/put helpers should be used when accessing an instance
|
||||||
* outside of a lifetime-guarded section. In general, this
|
* outside of a lifetime-guarded section. In general, this
|
||||||
* is only needed for handling filters shared across tasks.
|
* is only needed for handling filters shared across tasks.
|
||||||
|
* @log: true if all actions except for SECCOMP_RET_ALLOW should be logged
|
||||||
* @prev: points to a previously installed, or inherited, filter
|
* @prev: points to a previously installed, or inherited, filter
|
||||||
* @prog: the BPF program to evaluate
|
* @prog: the BPF program to evaluate
|
||||||
*
|
*
|
||||||
|
@ -57,6 +60,7 @@
|
||||||
*/
|
*/
|
||||||
struct seccomp_filter {
|
struct seccomp_filter {
|
||||||
refcount_t usage;
|
refcount_t usage;
|
||||||
|
bool log;
|
||||||
struct seccomp_filter *prev;
|
struct seccomp_filter *prev;
|
||||||
struct bpf_prog *prog;
|
struct bpf_prog *prog;
|
||||||
};
|
};
|
||||||
|
@ -171,10 +175,15 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
|
||||||
/**
|
/**
|
||||||
* seccomp_run_filters - evaluates all seccomp filters against @sd
|
* seccomp_run_filters - evaluates all seccomp filters against @sd
|
||||||
* @sd: optional seccomp data to be passed to filters
|
* @sd: optional seccomp data to be passed to filters
|
||||||
|
* @match: stores struct seccomp_filter that resulted in the return value,
|
||||||
|
* unless filter returned SECCOMP_RET_ALLOW, in which case it will
|
||||||
|
* be unchanged.
|
||||||
*
|
*
|
||||||
* Returns valid seccomp BPF response codes.
|
* Returns valid seccomp BPF response codes.
|
||||||
*/
|
*/
|
||||||
static u32 seccomp_run_filters(const struct seccomp_data *sd)
|
#define ACTION_ONLY(ret) ((s32)((ret) & (SECCOMP_RET_ACTION_FULL)))
|
||||||
|
static u32 seccomp_run_filters(const struct seccomp_data *sd,
|
||||||
|
struct seccomp_filter **match)
|
||||||
{
|
{
|
||||||
struct seccomp_data sd_local;
|
struct seccomp_data sd_local;
|
||||||
u32 ret = SECCOMP_RET_ALLOW;
|
u32 ret = SECCOMP_RET_ALLOW;
|
||||||
|
@ -184,7 +193,7 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd)
|
||||||
|
|
||||||
/* Ensure unexpected behavior doesn't result in failing open. */
|
/* Ensure unexpected behavior doesn't result in failing open. */
|
||||||
if (unlikely(WARN_ON(f == NULL)))
|
if (unlikely(WARN_ON(f == NULL)))
|
||||||
return SECCOMP_RET_KILL;
|
return SECCOMP_RET_KILL_PROCESS;
|
||||||
|
|
||||||
if (!sd) {
|
if (!sd) {
|
||||||
populate_seccomp_data(&sd_local);
|
populate_seccomp_data(&sd_local);
|
||||||
|
@ -198,8 +207,10 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd)
|
||||||
for (; f; f = f->prev) {
|
for (; f; f = f->prev) {
|
||||||
u32 cur_ret = BPF_PROG_RUN(f->prog, sd);
|
u32 cur_ret = BPF_PROG_RUN(f->prog, sd);
|
||||||
|
|
||||||
if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
|
if (ACTION_ONLY(cur_ret) < ACTION_ONLY(ret)) {
|
||||||
ret = cur_ret;
|
ret = cur_ret;
|
||||||
|
*match = f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -444,6 +455,10 @@ static long seccomp_attach_filter(unsigned int flags,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set log flag, if present. */
|
||||||
|
if (flags & SECCOMP_FILTER_FLAG_LOG)
|
||||||
|
filter->log = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is an existing filter, make it the prev and don't drop its
|
* If there is an existing filter, make it the prev and don't drop its
|
||||||
* task reference.
|
* task reference.
|
||||||
|
@ -514,6 +529,65 @@ static void seccomp_send_sigsys(int syscall, int reason)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SECCOMP_FILTER */
|
#endif /* CONFIG_SECCOMP_FILTER */
|
||||||
|
|
||||||
|
/* For use with seccomp_actions_logged */
|
||||||
|
#define SECCOMP_LOG_KILL_PROCESS (1 << 0)
|
||||||
|
#define SECCOMP_LOG_KILL_THREAD (1 << 1)
|
||||||
|
#define SECCOMP_LOG_TRAP (1 << 2)
|
||||||
|
#define SECCOMP_LOG_ERRNO (1 << 3)
|
||||||
|
#define SECCOMP_LOG_TRACE (1 << 4)
|
||||||
|
#define SECCOMP_LOG_LOG (1 << 5)
|
||||||
|
#define SECCOMP_LOG_ALLOW (1 << 6)
|
||||||
|
|
||||||
|
static u32 seccomp_actions_logged = SECCOMP_LOG_KILL_PROCESS |
|
||||||
|
SECCOMP_LOG_KILL_THREAD |
|
||||||
|
SECCOMP_LOG_TRAP |
|
||||||
|
SECCOMP_LOG_ERRNO |
|
||||||
|
SECCOMP_LOG_TRACE |
|
||||||
|
SECCOMP_LOG_LOG;
|
||||||
|
|
||||||
|
static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
|
||||||
|
bool requested)
|
||||||
|
{
|
||||||
|
bool log = false;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case SECCOMP_RET_ALLOW:
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_TRAP:
|
||||||
|
log = requested && seccomp_actions_logged & SECCOMP_LOG_TRAP;
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_ERRNO:
|
||||||
|
log = requested && seccomp_actions_logged & SECCOMP_LOG_ERRNO;
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_TRACE:
|
||||||
|
log = requested && seccomp_actions_logged & SECCOMP_LOG_TRACE;
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_LOG:
|
||||||
|
log = seccomp_actions_logged & SECCOMP_LOG_LOG;
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_KILL_THREAD:
|
||||||
|
log = seccomp_actions_logged & SECCOMP_LOG_KILL_THREAD;
|
||||||
|
break;
|
||||||
|
case SECCOMP_RET_KILL_PROCESS:
|
||||||
|
default:
|
||||||
|
log = seccomp_actions_logged & SECCOMP_LOG_KILL_PROCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Force an audit message to be emitted when the action is RET_KILL_*,
|
||||||
|
* RET_LOG, or the FILTER_FLAG_LOG bit was set and the action is
|
||||||
|
* allowed to be logged by the admin.
|
||||||
|
*/
|
||||||
|
if (log)
|
||||||
|
return __audit_seccomp(syscall, signr, action);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let the audit subsystem decide if the action should be audited based
|
||||||
|
* on whether the current task itself is being audited.
|
||||||
|
*/
|
||||||
|
return audit_seccomp(syscall, signr, action);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Secure computing mode 1 allows only read/write/exit/sigreturn.
|
* Secure computing mode 1 allows only read/write/exit/sigreturn.
|
||||||
* To be fully secure this must be combined with rlimit
|
* To be fully secure this must be combined with rlimit
|
||||||
|
@ -539,7 +613,7 @@ static void __secure_computing_strict(int this_syscall)
|
||||||
#ifdef SECCOMP_DEBUG
|
#ifdef SECCOMP_DEBUG
|
||||||
dump_stack();
|
dump_stack();
|
||||||
#endif
|
#endif
|
||||||
audit_seccomp(this_syscall, SIGKILL, SECCOMP_RET_KILL);
|
seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL_THREAD, true);
|
||||||
do_exit(SIGKILL);
|
do_exit(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,6 +640,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
||||||
const bool recheck_after_trace)
|
const bool recheck_after_trace)
|
||||||
{
|
{
|
||||||
u32 filter_ret, action;
|
u32 filter_ret, action;
|
||||||
|
struct seccomp_filter *match = NULL;
|
||||||
int data;
|
int data;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -574,9 +649,9 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
||||||
*/
|
*/
|
||||||
rmb();
|
rmb();
|
||||||
|
|
||||||
filter_ret = seccomp_run_filters(sd);
|
filter_ret = seccomp_run_filters(sd, &match);
|
||||||
data = filter_ret & SECCOMP_RET_DATA;
|
data = filter_ret & SECCOMP_RET_DATA;
|
||||||
action = filter_ret & SECCOMP_RET_ACTION;
|
action = filter_ret & SECCOMP_RET_ACTION_FULL;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case SECCOMP_RET_ERRNO:
|
case SECCOMP_RET_ERRNO:
|
||||||
|
@ -637,14 +712,25 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case SECCOMP_RET_ALLOW:
|
case SECCOMP_RET_LOG:
|
||||||
|
seccomp_log(this_syscall, 0, action, true);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case SECCOMP_RET_KILL:
|
case SECCOMP_RET_ALLOW:
|
||||||
|
/*
|
||||||
|
* Note that the "match" filter will always be NULL for
|
||||||
|
* this action since SECCOMP_RET_ALLOW is the starting
|
||||||
|
* state in seccomp_run_filters().
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case SECCOMP_RET_KILL_THREAD:
|
||||||
|
case SECCOMP_RET_KILL_PROCESS:
|
||||||
default:
|
default:
|
||||||
audit_seccomp(this_syscall, SIGSYS, action);
|
seccomp_log(this_syscall, SIGSYS, action, true);
|
||||||
/* Dump core only if this is the last remaining thread. */
|
/* Dump core only if this is the last remaining thread. */
|
||||||
if (get_nr_threads(current) == 1) {
|
if (action == SECCOMP_RET_KILL_PROCESS ||
|
||||||
|
get_nr_threads(current) == 1) {
|
||||||
siginfo_t info;
|
siginfo_t info;
|
||||||
|
|
||||||
/* Show the original registers in the dump. */
|
/* Show the original registers in the dump. */
|
||||||
|
@ -653,13 +739,16 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
||||||
seccomp_init_siginfo(&info, this_syscall, data);
|
seccomp_init_siginfo(&info, this_syscall, data);
|
||||||
do_coredump(&info);
|
do_coredump(&info);
|
||||||
}
|
}
|
||||||
do_exit(SIGSYS);
|
if (action == SECCOMP_RET_KILL_PROCESS)
|
||||||
|
do_group_exit(SIGSYS);
|
||||||
|
else
|
||||||
|
do_exit(SIGSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable();
|
unreachable();
|
||||||
|
|
||||||
skip:
|
skip:
|
||||||
audit_seccomp(this_syscall, 0, action);
|
seccomp_log(this_syscall, 0, action, match ? match->log : false);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -794,6 +883,29 @@ static inline long seccomp_set_mode_filter(unsigned int flags,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static long seccomp_get_action_avail(const char __user *uaction)
|
||||||
|
{
|
||||||
|
u32 action;
|
||||||
|
|
||||||
|
if (copy_from_user(&action, uaction, sizeof(action)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case SECCOMP_RET_KILL_PROCESS:
|
||||||
|
case SECCOMP_RET_KILL_THREAD:
|
||||||
|
case SECCOMP_RET_TRAP:
|
||||||
|
case SECCOMP_RET_ERRNO:
|
||||||
|
case SECCOMP_RET_TRACE:
|
||||||
|
case SECCOMP_RET_LOG:
|
||||||
|
case SECCOMP_RET_ALLOW:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Common entry point for both prctl and syscall. */
|
/* Common entry point for both prctl and syscall. */
|
||||||
static long do_seccomp(unsigned int op, unsigned int flags,
|
static long do_seccomp(unsigned int op, unsigned int flags,
|
||||||
const char __user *uargs)
|
const char __user *uargs)
|
||||||
|
@ -805,6 +917,11 @@ static long do_seccomp(unsigned int op, unsigned int flags,
|
||||||
return seccomp_set_mode_strict();
|
return seccomp_set_mode_strict();
|
||||||
case SECCOMP_SET_MODE_FILTER:
|
case SECCOMP_SET_MODE_FILTER:
|
||||||
return seccomp_set_mode_filter(flags, uargs);
|
return seccomp_set_mode_filter(flags, uargs);
|
||||||
|
case SECCOMP_GET_ACTION_AVAIL:
|
||||||
|
if (flags != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return seccomp_get_action_avail(uargs);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -922,3 +1039,185 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SYSCTL
|
||||||
|
|
||||||
|
/* Human readable action names for friendly sysctl interaction */
|
||||||
|
#define SECCOMP_RET_KILL_PROCESS_NAME "kill_process"
|
||||||
|
#define SECCOMP_RET_KILL_THREAD_NAME "kill_thread"
|
||||||
|
#define SECCOMP_RET_TRAP_NAME "trap"
|
||||||
|
#define SECCOMP_RET_ERRNO_NAME "errno"
|
||||||
|
#define SECCOMP_RET_TRACE_NAME "trace"
|
||||||
|
#define SECCOMP_RET_LOG_NAME "log"
|
||||||
|
#define SECCOMP_RET_ALLOW_NAME "allow"
|
||||||
|
|
||||||
|
static const char seccomp_actions_avail[] =
|
||||||
|
SECCOMP_RET_KILL_PROCESS_NAME " "
|
||||||
|
SECCOMP_RET_KILL_THREAD_NAME " "
|
||||||
|
SECCOMP_RET_TRAP_NAME " "
|
||||||
|
SECCOMP_RET_ERRNO_NAME " "
|
||||||
|
SECCOMP_RET_TRACE_NAME " "
|
||||||
|
SECCOMP_RET_LOG_NAME " "
|
||||||
|
SECCOMP_RET_ALLOW_NAME;
|
||||||
|
|
||||||
|
struct seccomp_log_name {
|
||||||
|
u32 log;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct seccomp_log_name seccomp_log_names[] = {
|
||||||
|
{ SECCOMP_LOG_KILL_PROCESS, SECCOMP_RET_KILL_PROCESS_NAME },
|
||||||
|
{ SECCOMP_LOG_KILL_THREAD, SECCOMP_RET_KILL_THREAD_NAME },
|
||||||
|
{ SECCOMP_LOG_TRAP, SECCOMP_RET_TRAP_NAME },
|
||||||
|
{ SECCOMP_LOG_ERRNO, SECCOMP_RET_ERRNO_NAME },
|
||||||
|
{ SECCOMP_LOG_TRACE, SECCOMP_RET_TRACE_NAME },
|
||||||
|
{ SECCOMP_LOG_LOG, SECCOMP_RET_LOG_NAME },
|
||||||
|
{ SECCOMP_LOG_ALLOW, SECCOMP_RET_ALLOW_NAME },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool seccomp_names_from_actions_logged(char *names, size_t size,
|
||||||
|
u32 actions_logged)
|
||||||
|
{
|
||||||
|
const struct seccomp_log_name *cur;
|
||||||
|
bool append_space = false;
|
||||||
|
|
||||||
|
for (cur = seccomp_log_names; cur->name && size; cur++) {
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!(actions_logged & cur->log))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (append_space) {
|
||||||
|
ret = strscpy(names, " ", size);
|
||||||
|
if (ret < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
names += ret;
|
||||||
|
size -= ret;
|
||||||
|
} else
|
||||||
|
append_space = true;
|
||||||
|
|
||||||
|
ret = strscpy(names, cur->name, size);
|
||||||
|
if (ret < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
names += ret;
|
||||||
|
size -= ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool seccomp_action_logged_from_name(u32 *action_logged,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
const struct seccomp_log_name *cur;
|
||||||
|
|
||||||
|
for (cur = seccomp_log_names; cur->name; cur++) {
|
||||||
|
if (!strcmp(cur->name, name)) {
|
||||||
|
*action_logged = cur->log;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool seccomp_actions_logged_from_names(u32 *actions_logged, char *names)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
*actions_logged = 0;
|
||||||
|
while ((name = strsep(&names, " ")) && *name) {
|
||||||
|
u32 action_logged = 0;
|
||||||
|
|
||||||
|
if (!seccomp_action_logged_from_name(&action_logged, name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*actions_logged |= action_logged;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int seccomp_actions_logged_handler(struct ctl_table *ro_table, int write,
|
||||||
|
void __user *buffer, size_t *lenp,
|
||||||
|
loff_t *ppos)
|
||||||
|
{
|
||||||
|
char names[sizeof(seccomp_actions_avail)];
|
||||||
|
struct ctl_table table;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (write && !capable(CAP_SYS_ADMIN))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
|
memset(names, 0, sizeof(names));
|
||||||
|
|
||||||
|
if (!write) {
|
||||||
|
if (!seccomp_names_from_actions_logged(names, sizeof(names),
|
||||||
|
seccomp_actions_logged))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
table = *ro_table;
|
||||||
|
table.data = names;
|
||||||
|
table.maxlen = sizeof(names);
|
||||||
|
ret = proc_dostring(&table, write, buffer, lenp, ppos);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
u32 actions_logged;
|
||||||
|
|
||||||
|
if (!seccomp_actions_logged_from_names(&actions_logged,
|
||||||
|
table.data))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (actions_logged & SECCOMP_LOG_ALLOW)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
seccomp_actions_logged = actions_logged;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ctl_path seccomp_sysctl_path[] = {
|
||||||
|
{ .procname = "kernel", },
|
||||||
|
{ .procname = "seccomp", },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ctl_table seccomp_sysctl_table[] = {
|
||||||
|
{
|
||||||
|
.procname = "actions_avail",
|
||||||
|
.data = (void *) &seccomp_actions_avail,
|
||||||
|
.maxlen = sizeof(seccomp_actions_avail),
|
||||||
|
.mode = 0444,
|
||||||
|
.proc_handler = proc_dostring,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.procname = "actions_logged",
|
||||||
|
.mode = 0644,
|
||||||
|
.proc_handler = seccomp_actions_logged_handler,
|
||||||
|
},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init seccomp_sysctl_init(void)
|
||||||
|
{
|
||||||
|
struct ctl_table_header *hdr;
|
||||||
|
|
||||||
|
hdr = register_sysctl_paths(seccomp_sysctl_path, seccomp_sysctl_table);
|
||||||
|
if (!hdr)
|
||||||
|
pr_warn("seccomp: sysctl registration failed\n");
|
||||||
|
else
|
||||||
|
kmemleak_not_leak(hdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_initcall(seccomp_sysctl_init)
|
||||||
|
|
||||||
|
#endif /* CONFIG_SYSCTL */
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
TEST_GEN_PROGS := seccomp_bpf
|
all:
|
||||||
CFLAGS += -Wl,-no-as-needed -Wall
|
|
||||||
LDFLAGS += -lpthread
|
|
||||||
|
|
||||||
include ../lib.mk
|
include ../lib.mk
|
||||||
|
|
||||||
$(TEST_GEN_PROGS): seccomp_bpf.c ../kselftest_harness.h
|
.PHONY: all clean
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
|
||||||
|
BINARIES := seccomp_bpf seccomp_benchmark
|
||||||
|
CFLAGS += -Wl,-no-as-needed -Wall
|
||||||
|
|
||||||
|
seccomp_bpf: seccomp_bpf.c ../kselftest_harness.h
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -lpthread $< -o $@
|
||||||
|
|
||||||
|
TEST_PROGS += $(BINARIES)
|
||||||
|
EXTRA_CLEAN := $(BINARIES)
|
||||||
|
|
||||||
|
all: $(BINARIES)
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Strictly speaking, this is not a test. But it can report during test
|
||||||
|
* runs so relative performace can be measured.
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||||
|
|
||||||
|
unsigned long long timing(clockid_t clk_id, unsigned long long samples)
|
||||||
|
{
|
||||||
|
pid_t pid, ret;
|
||||||
|
unsigned long long i;
|
||||||
|
struct timespec start, finish;
|
||||||
|
|
||||||
|
pid = getpid();
|
||||||
|
assert(clock_gettime(clk_id, &start) == 0);
|
||||||
|
for (i = 0; i < samples; i++) {
|
||||||
|
ret = syscall(__NR_getpid);
|
||||||
|
assert(pid == ret);
|
||||||
|
}
|
||||||
|
assert(clock_gettime(clk_id, &finish) == 0);
|
||||||
|
|
||||||
|
i = finish.tv_sec - start.tv_sec;
|
||||||
|
i *= 1000000000;
|
||||||
|
i += finish.tv_nsec - start.tv_nsec;
|
||||||
|
|
||||||
|
printf("%lu.%09lu - %lu.%09lu = %llu\n",
|
||||||
|
finish.tv_sec, finish.tv_nsec,
|
||||||
|
start.tv_sec, start.tv_nsec,
|
||||||
|
i);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long calibrate(void)
|
||||||
|
{
|
||||||
|
unsigned long long i;
|
||||||
|
|
||||||
|
printf("Calibrating reasonable sample size...\n");
|
||||||
|
|
||||||
|
for (i = 5; ; i++) {
|
||||||
|
unsigned long long samples = 1 << i;
|
||||||
|
|
||||||
|
/* Find something that takes more than 5 seconds to run. */
|
||||||
|
if (timing(CLOCK_REALTIME, samples) / 1000000000ULL > 5)
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct sock_filter filter[] = {
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
};
|
||||||
|
struct sock_fprog prog = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(filter),
|
||||||
|
.filter = filter,
|
||||||
|
};
|
||||||
|
long ret;
|
||||||
|
unsigned long long samples;
|
||||||
|
unsigned long long native, filtered;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
samples = strtoull(argv[1], NULL, 0);
|
||||||
|
else
|
||||||
|
samples = calibrate();
|
||||||
|
|
||||||
|
printf("Benchmarking %llu samples...\n", samples);
|
||||||
|
|
||||||
|
native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
|
||||||
|
printf("getpid native: %llu ns\n", native);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
filtered = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
|
||||||
|
printf("getpid RET_ALLOW: %llu ns\n", filtered);
|
||||||
|
|
||||||
|
printf("Estimated seccomp overhead per syscall: %llu ns\n",
|
||||||
|
filtered - native);
|
||||||
|
|
||||||
|
if (filtered == native)
|
||||||
|
printf("Trying running again with more samples.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -68,17 +68,7 @@
|
||||||
#define SECCOMP_MODE_FILTER 2
|
#define SECCOMP_MODE_FILTER 2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SECCOMP_RET_KILL
|
#ifndef SECCOMP_RET_ALLOW
|
||||||
#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */
|
|
||||||
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
|
|
||||||
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
|
|
||||||
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
|
|
||||||
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
|
|
||||||
|
|
||||||
/* Masks for the return value sections. */
|
|
||||||
#define SECCOMP_RET_ACTION 0x7fff0000U
|
|
||||||
#define SECCOMP_RET_DATA 0x0000ffffU
|
|
||||||
|
|
||||||
struct seccomp_data {
|
struct seccomp_data {
|
||||||
int nr;
|
int nr;
|
||||||
__u32 arch;
|
__u32 arch;
|
||||||
|
@ -87,6 +77,70 @@ struct seccomp_data {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_RET_KILL_PROCESS
|
||||||
|
#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */
|
||||||
|
#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */
|
||||||
|
#endif
|
||||||
|
#ifndef SECCOMP_RET_KILL
|
||||||
|
#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD
|
||||||
|
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
|
||||||
|
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
|
||||||
|
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
|
||||||
|
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */
|
||||||
|
#endif
|
||||||
|
#ifndef SECCOMP_RET_LOG
|
||||||
|
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __NR_seccomp
|
||||||
|
# if defined(__i386__)
|
||||||
|
# define __NR_seccomp 354
|
||||||
|
# elif defined(__x86_64__)
|
||||||
|
# define __NR_seccomp 317
|
||||||
|
# elif defined(__arm__)
|
||||||
|
# define __NR_seccomp 383
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
# define __NR_seccomp 277
|
||||||
|
# elif defined(__hppa__)
|
||||||
|
# define __NR_seccomp 338
|
||||||
|
# elif defined(__powerpc__)
|
||||||
|
# define __NR_seccomp 358
|
||||||
|
# elif defined(__s390__)
|
||||||
|
# define __NR_seccomp 348
|
||||||
|
# else
|
||||||
|
# warning "seccomp syscall number unknown for this architecture"
|
||||||
|
# define __NR_seccomp 0xffff
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_SET_MODE_STRICT
|
||||||
|
#define SECCOMP_SET_MODE_STRICT 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_SET_MODE_FILTER
|
||||||
|
#define SECCOMP_SET_MODE_FILTER 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_GET_ACTION_AVAIL
|
||||||
|
#define SECCOMP_GET_ACTION_AVAIL 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_FILTER_FLAG_TSYNC
|
||||||
|
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SECCOMP_FILTER_FLAG_LOG
|
||||||
|
#define SECCOMP_FILTER_FLAG_LOG 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef seccomp
|
||||||
|
int seccomp(unsigned int op, unsigned int flags, void *args)
|
||||||
|
{
|
||||||
|
errno = 0;
|
||||||
|
return syscall(__NR_seccomp, op, flags, args);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
|
#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
|
||||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
@ -136,7 +190,7 @@ TEST(no_new_privs_support)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tests kernel support by checking for a copy_from_user() fault on * NULL. */
|
/* Tests kernel support by checking for a copy_from_user() fault on NULL. */
|
||||||
TEST(mode_filter_support)
|
TEST(mode_filter_support)
|
||||||
{
|
{
|
||||||
long ret;
|
long ret;
|
||||||
|
@ -342,6 +396,28 @@ TEST(empty_prog)
|
||||||
EXPECT_EQ(EINVAL, errno);
|
EXPECT_EQ(EINVAL, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(log_all)
|
||||||
|
{
|
||||||
|
struct sock_filter filter[] = {
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG),
|
||||||
|
};
|
||||||
|
struct sock_fprog prog = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(filter),
|
||||||
|
.filter = filter,
|
||||||
|
};
|
||||||
|
long ret;
|
||||||
|
pid_t parent = getppid();
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
/* getppid() should succeed and be logged (no check for logging) */
|
||||||
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS)
|
TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
struct sock_filter filter[] = {
|
||||||
|
@ -520,6 +596,117 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is a thread task to die via seccomp filter violation. */
|
||||||
|
void *kill_thread(void *data)
|
||||||
|
{
|
||||||
|
bool die = (bool)data;
|
||||||
|
|
||||||
|
if (die) {
|
||||||
|
prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
|
||||||
|
return (void *)SIBLING_EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)SIBLING_EXIT_UNKILLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare a thread that will kill itself or both of us. */
|
||||||
|
void kill_thread_or_group(struct __test_metadata *_metadata, bool kill_process)
|
||||||
|
{
|
||||||
|
pthread_t thread;
|
||||||
|
void *status;
|
||||||
|
/* Kill only when calling __NR_prctl. */
|
||||||
|
struct sock_filter filter_thread[] = {
|
||||||
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
||||||
|
offsetof(struct seccomp_data, nr)),
|
||||||
|
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_THREAD),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
};
|
||||||
|
struct sock_fprog prog_thread = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(filter_thread),
|
||||||
|
.filter = filter_thread,
|
||||||
|
};
|
||||||
|
struct sock_filter filter_process[] = {
|
||||||
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
||||||
|
offsetof(struct seccomp_data, nr)),
|
||||||
|
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_PROCESS),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
};
|
||||||
|
struct sock_fprog prog_process = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(filter_process),
|
||||||
|
.filter = filter_process,
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||||
|
TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0,
|
||||||
|
kill_process ? &prog_process : &prog_thread));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the KILL_THREAD rule again to make sure that the KILL_PROCESS
|
||||||
|
* flag cannot be downgraded by a new filter.
|
||||||
|
*/
|
||||||
|
ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog_thread));
|
||||||
|
|
||||||
|
/* Start a thread that will exit immediately. */
|
||||||
|
ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)false));
|
||||||
|
ASSERT_EQ(0, pthread_join(thread, &status));
|
||||||
|
ASSERT_EQ(SIBLING_EXIT_UNKILLED, (unsigned long)status);
|
||||||
|
|
||||||
|
/* Start a thread that will die immediately. */
|
||||||
|
ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)true));
|
||||||
|
ASSERT_EQ(0, pthread_join(thread, &status));
|
||||||
|
ASSERT_NE(SIBLING_EXIT_FAILURE, (unsigned long)status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we get here, only the spawned thread died. Let the parent know
|
||||||
|
* the whole process didn't die (i.e. this thread, the spawner,
|
||||||
|
* stayed running).
|
||||||
|
*/
|
||||||
|
exit(42);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KILL_thread)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
pid_t child_pid;
|
||||||
|
|
||||||
|
child_pid = fork();
|
||||||
|
ASSERT_LE(0, child_pid);
|
||||||
|
if (child_pid == 0) {
|
||||||
|
kill_thread_or_group(_metadata, false);
|
||||||
|
_exit(38);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
|
||||||
|
|
||||||
|
/* If only the thread was killed, we'll see exit 42. */
|
||||||
|
ASSERT_TRUE(WIFEXITED(status));
|
||||||
|
ASSERT_EQ(42, WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(KILL_process)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
pid_t child_pid;
|
||||||
|
|
||||||
|
child_pid = fork();
|
||||||
|
ASSERT_LE(0, child_pid);
|
||||||
|
if (child_pid == 0) {
|
||||||
|
kill_thread_or_group(_metadata, true);
|
||||||
|
_exit(38);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
|
||||||
|
|
||||||
|
/* If the entire process was killed, we'll see SIGSYS. */
|
||||||
|
ASSERT_TRUE(WIFSIGNALED(status));
|
||||||
|
ASSERT_EQ(SIGSYS, WTERMSIG(status));
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO(wad) add 64-bit versus 32-bit arg tests. */
|
/* TODO(wad) add 64-bit versus 32-bit arg tests. */
|
||||||
TEST(arg_out_of_range)
|
TEST(arg_out_of_range)
|
||||||
{
|
{
|
||||||
|
@ -541,26 +728,30 @@ TEST(arg_out_of_range)
|
||||||
EXPECT_EQ(EINVAL, errno);
|
EXPECT_EQ(EINVAL, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ERRNO_FILTER(name, errno) \
|
||||||
|
struct sock_filter _read_filter_##name[] = { \
|
||||||
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, \
|
||||||
|
offsetof(struct seccomp_data, nr)), \
|
||||||
|
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1), \
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | errno), \
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), \
|
||||||
|
}; \
|
||||||
|
struct sock_fprog prog_##name = { \
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(_read_filter_##name), \
|
||||||
|
.filter = _read_filter_##name, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure basic errno values are correctly passed through a filter. */
|
||||||
TEST(ERRNO_valid)
|
TEST(ERRNO_valid)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
ERRNO_FILTER(valid, E2BIG);
|
||||||
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
|
||||||
offsetof(struct seccomp_data, nr)),
|
|
||||||
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | E2BIG),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
|
||||||
};
|
|
||||||
struct sock_fprog prog = {
|
|
||||||
.len = (unsigned short)ARRAY_SIZE(filter),
|
|
||||||
.filter = filter,
|
|
||||||
};
|
|
||||||
long ret;
|
long ret;
|
||||||
pid_t parent = getppid();
|
pid_t parent = getppid();
|
||||||
|
|
||||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_valid);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
EXPECT_EQ(parent, syscall(__NR_getppid));
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
@ -568,26 +759,17 @@ TEST(ERRNO_valid)
|
||||||
EXPECT_EQ(E2BIG, errno);
|
EXPECT_EQ(E2BIG, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make sure an errno of zero is correctly handled by the arch code. */
|
||||||
TEST(ERRNO_zero)
|
TEST(ERRNO_zero)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
ERRNO_FILTER(zero, 0);
|
||||||
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
|
||||||
offsetof(struct seccomp_data, nr)),
|
|
||||||
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 0),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
|
||||||
};
|
|
||||||
struct sock_fprog prog = {
|
|
||||||
.len = (unsigned short)ARRAY_SIZE(filter),
|
|
||||||
.filter = filter,
|
|
||||||
};
|
|
||||||
long ret;
|
long ret;
|
||||||
pid_t parent = getppid();
|
pid_t parent = getppid();
|
||||||
|
|
||||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_zero);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
EXPECT_EQ(parent, syscall(__NR_getppid));
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
@ -595,26 +777,21 @@ TEST(ERRNO_zero)
|
||||||
EXPECT_EQ(0, read(0, NULL, 0));
|
EXPECT_EQ(0, read(0, NULL, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The SECCOMP_RET_DATA mask is 16 bits wide, but errno is smaller.
|
||||||
|
* This tests that the errno value gets capped correctly, fixed by
|
||||||
|
* 580c57f10768 ("seccomp: cap SECCOMP_RET_ERRNO data to MAX_ERRNO").
|
||||||
|
*/
|
||||||
TEST(ERRNO_capped)
|
TEST(ERRNO_capped)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
ERRNO_FILTER(capped, 4096);
|
||||||
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
|
||||||
offsetof(struct seccomp_data, nr)),
|
|
||||||
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 4096),
|
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
|
||||||
};
|
|
||||||
struct sock_fprog prog = {
|
|
||||||
.len = (unsigned short)ARRAY_SIZE(filter),
|
|
||||||
.filter = filter,
|
|
||||||
};
|
|
||||||
long ret;
|
long ret;
|
||||||
pid_t parent = getppid();
|
pid_t parent = getppid();
|
||||||
|
|
||||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_capped);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
EXPECT_EQ(parent, syscall(__NR_getppid));
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
@ -622,6 +799,37 @@ TEST(ERRNO_capped)
|
||||||
EXPECT_EQ(4095, errno);
|
EXPECT_EQ(4095, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Filters are processed in reverse order: last applied is executed first.
|
||||||
|
* Since only the SECCOMP_RET_ACTION mask is tested for return values, the
|
||||||
|
* SECCOMP_RET_DATA mask results will follow the most recently applied
|
||||||
|
* matching filter return (and not the lowest or highest value).
|
||||||
|
*/
|
||||||
|
TEST(ERRNO_order)
|
||||||
|
{
|
||||||
|
ERRNO_FILTER(first, 11);
|
||||||
|
ERRNO_FILTER(second, 13);
|
||||||
|
ERRNO_FILTER(third, 12);
|
||||||
|
long ret;
|
||||||
|
pid_t parent = getppid();
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_first);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_second);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_third);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
EXPECT_EQ(-1, read(0, NULL, 0));
|
||||||
|
EXPECT_EQ(12, errno);
|
||||||
|
}
|
||||||
|
|
||||||
FIXTURE_DATA(TRAP) {
|
FIXTURE_DATA(TRAP) {
|
||||||
struct sock_fprog prog;
|
struct sock_fprog prog;
|
||||||
};
|
};
|
||||||
|
@ -735,6 +943,7 @@ TEST_F(TRAP, handler)
|
||||||
|
|
||||||
FIXTURE_DATA(precedence) {
|
FIXTURE_DATA(precedence) {
|
||||||
struct sock_fprog allow;
|
struct sock_fprog allow;
|
||||||
|
struct sock_fprog log;
|
||||||
struct sock_fprog trace;
|
struct sock_fprog trace;
|
||||||
struct sock_fprog error;
|
struct sock_fprog error;
|
||||||
struct sock_fprog trap;
|
struct sock_fprog trap;
|
||||||
|
@ -746,6 +955,13 @@ FIXTURE_SETUP(precedence)
|
||||||
struct sock_filter allow_insns[] = {
|
struct sock_filter allow_insns[] = {
|
||||||
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
};
|
};
|
||||||
|
struct sock_filter log_insns[] = {
|
||||||
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
||||||
|
offsetof(struct seccomp_data, nr)),
|
||||||
|
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG),
|
||||||
|
};
|
||||||
struct sock_filter trace_insns[] = {
|
struct sock_filter trace_insns[] = {
|
||||||
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
||||||
offsetof(struct seccomp_data, nr)),
|
offsetof(struct seccomp_data, nr)),
|
||||||
|
@ -782,6 +998,7 @@ FIXTURE_SETUP(precedence)
|
||||||
memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \
|
memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \
|
||||||
self->_x.len = (unsigned short)ARRAY_SIZE(_x##_insns)
|
self->_x.len = (unsigned short)ARRAY_SIZE(_x##_insns)
|
||||||
FILTER_ALLOC(allow);
|
FILTER_ALLOC(allow);
|
||||||
|
FILTER_ALLOC(log);
|
||||||
FILTER_ALLOC(trace);
|
FILTER_ALLOC(trace);
|
||||||
FILTER_ALLOC(error);
|
FILTER_ALLOC(error);
|
||||||
FILTER_ALLOC(trap);
|
FILTER_ALLOC(trap);
|
||||||
|
@ -792,6 +1009,7 @@ FIXTURE_TEARDOWN(precedence)
|
||||||
{
|
{
|
||||||
#define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)
|
#define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)
|
||||||
FILTER_FREE(allow);
|
FILTER_FREE(allow);
|
||||||
|
FILTER_FREE(log);
|
||||||
FILTER_FREE(trace);
|
FILTER_FREE(trace);
|
||||||
FILTER_FREE(error);
|
FILTER_FREE(error);
|
||||||
FILTER_FREE(trap);
|
FILTER_FREE(trap);
|
||||||
|
@ -809,6 +1027,8 @@ TEST_F(precedence, allow_ok)
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
|
@ -833,6 +1053,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS)
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
|
@ -864,6 +1086,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS)
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
|
||||||
|
@ -885,6 +1109,8 @@ TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS)
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
|
@ -910,6 +1136,8 @@ TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS)
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
|
@ -931,6 +1159,8 @@ TEST_F(precedence, errno_is_third)
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
|
@ -949,6 +1179,8 @@ TEST_F(precedence, errno_is_third_in_any_order)
|
||||||
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
|
@ -971,6 +1203,8 @@ TEST_F(precedence, trace_is_fourth)
|
||||||
|
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
/* Should work just fine. */
|
/* Should work just fine. */
|
||||||
|
@ -992,12 +1226,54 @@ TEST_F(precedence, trace_is_fourth_in_any_order)
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
/* Should work just fine. */
|
/* Should work just fine. */
|
||||||
EXPECT_EQ(parent, syscall(__NR_getppid));
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
/* No ptracer */
|
/* No ptracer */
|
||||||
EXPECT_EQ(-1, syscall(__NR_getpid));
|
EXPECT_EQ(-1, syscall(__NR_getpid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(precedence, log_is_fifth)
|
||||||
|
{
|
||||||
|
pid_t mypid, parent;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
mypid = getpid();
|
||||||
|
parent = getppid();
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
/* Should work just fine. */
|
||||||
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
/* Should also work just fine */
|
||||||
|
EXPECT_EQ(mypid, syscall(__NR_getpid));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(precedence, log_is_fifth_in_any_order)
|
||||||
|
{
|
||||||
|
pid_t mypid, parent;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
mypid = getpid();
|
||||||
|
parent = getppid();
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
/* Should work just fine. */
|
||||||
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
/* Should also work just fine */
|
||||||
|
EXPECT_EQ(mypid, syscall(__NR_getpid));
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef PTRACE_O_TRACESECCOMP
|
#ifndef PTRACE_O_TRACESECCOMP
|
||||||
#define PTRACE_O_TRACESECCOMP 0x00000080
|
#define PTRACE_O_TRACESECCOMP 0x00000080
|
||||||
#endif
|
#endif
|
||||||
|
@ -1262,6 +1538,13 @@ TEST_F(TRACE_poke, getpid_runs_normally)
|
||||||
# error "Do not know how to find your architecture's registers and syscalls"
|
# error "Do not know how to find your architecture's registers and syscalls"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* When the syscall return can't be changed, stub out the tests for it. */
|
||||||
|
#ifdef SYSCALL_NUM_RET_SHARE_REG
|
||||||
|
# define EXPECT_SYSCALL_RETURN(val, action) EXPECT_EQ(-1, action)
|
||||||
|
#else
|
||||||
|
# define EXPECT_SYSCALL_RETURN(val, action) EXPECT_EQ(val, action)
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for
|
/* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for
|
||||||
* architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux).
|
* architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux).
|
||||||
*/
|
*/
|
||||||
|
@ -1357,7 +1640,7 @@ void change_syscall(struct __test_metadata *_metadata,
|
||||||
#ifdef SYSCALL_NUM_RET_SHARE_REG
|
#ifdef SYSCALL_NUM_RET_SHARE_REG
|
||||||
TH_LOG("Can't modify syscall return on this architecture");
|
TH_LOG("Can't modify syscall return on this architecture");
|
||||||
#else
|
#else
|
||||||
regs.SYSCALL_RET = 1;
|
regs.SYSCALL_RET = EPERM;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_GETREGS
|
#ifdef HAVE_GETREGS
|
||||||
|
@ -1426,6 +1709,8 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee,
|
||||||
|
|
||||||
if (nr == __NR_getpid)
|
if (nr == __NR_getpid)
|
||||||
change_syscall(_metadata, tracee, __NR_getppid);
|
change_syscall(_metadata, tracee, __NR_getppid);
|
||||||
|
if (nr == __NR_open)
|
||||||
|
change_syscall(_metadata, tracee, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
FIXTURE_DATA(TRACE_syscall) {
|
FIXTURE_DATA(TRACE_syscall) {
|
||||||
|
@ -1480,6 +1765,28 @@ FIXTURE_TEARDOWN(TRACE_syscall)
|
||||||
free(self->prog.filter);
|
free(self->prog.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TRACE_syscall, ptrace_syscall_redirected)
|
||||||
|
{
|
||||||
|
/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
|
||||||
|
teardown_trace_fixture(_metadata, self->tracer);
|
||||||
|
self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
|
||||||
|
true);
|
||||||
|
|
||||||
|
/* Tracer will redirect getpid to getppid. */
|
||||||
|
EXPECT_NE(self->mypid, syscall(__NR_getpid));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TRACE_syscall, ptrace_syscall_dropped)
|
||||||
|
{
|
||||||
|
/* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */
|
||||||
|
teardown_trace_fixture(_metadata, self->tracer);
|
||||||
|
self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL,
|
||||||
|
true);
|
||||||
|
|
||||||
|
/* Tracer should skip the open syscall, resulting in EPERM. */
|
||||||
|
EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_open));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TRACE_syscall, syscall_allowed)
|
TEST_F(TRACE_syscall, syscall_allowed)
|
||||||
{
|
{
|
||||||
long ret;
|
long ret;
|
||||||
|
@ -1520,13 +1827,8 @@ TEST_F(TRACE_syscall, syscall_dropped)
|
||||||
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
|
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
#ifdef SYSCALL_NUM_RET_SHARE_REG
|
|
||||||
/* gettid has been skipped */
|
|
||||||
EXPECT_EQ(-1, syscall(__NR_gettid));
|
|
||||||
#else
|
|
||||||
/* gettid has been skipped and an altered return value stored. */
|
/* gettid has been skipped and an altered return value stored. */
|
||||||
EXPECT_EQ(1, syscall(__NR_gettid));
|
EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_gettid));
|
||||||
#endif
|
|
||||||
EXPECT_NE(self->mytid, syscall(__NR_gettid));
|
EXPECT_NE(self->mytid, syscall(__NR_gettid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1557,6 +1859,7 @@ TEST_F(TRACE_syscall, skip_after_RET_TRACE)
|
||||||
ASSERT_EQ(0, ret);
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
/* Tracer will redirect getpid to getppid, and we should see EPERM. */
|
/* Tracer will redirect getpid to getppid, and we should see EPERM. */
|
||||||
|
errno = 0;
|
||||||
EXPECT_EQ(-1, syscall(__NR_getpid));
|
EXPECT_EQ(-1, syscall(__NR_getpid));
|
||||||
EXPECT_EQ(EPERM, errno);
|
EXPECT_EQ(EPERM, errno);
|
||||||
}
|
}
|
||||||
|
@ -1654,47 +1957,6 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS)
|
||||||
EXPECT_NE(self->mypid, syscall(__NR_getpid));
|
EXPECT_NE(self->mypid, syscall(__NR_getpid));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __NR_seccomp
|
|
||||||
# if defined(__i386__)
|
|
||||||
# define __NR_seccomp 354
|
|
||||||
# elif defined(__x86_64__)
|
|
||||||
# define __NR_seccomp 317
|
|
||||||
# elif defined(__arm__)
|
|
||||||
# define __NR_seccomp 383
|
|
||||||
# elif defined(__aarch64__)
|
|
||||||
# define __NR_seccomp 277
|
|
||||||
# elif defined(__hppa__)
|
|
||||||
# define __NR_seccomp 338
|
|
||||||
# elif defined(__powerpc__)
|
|
||||||
# define __NR_seccomp 358
|
|
||||||
# elif defined(__s390__)
|
|
||||||
# define __NR_seccomp 348
|
|
||||||
# else
|
|
||||||
# warning "seccomp syscall number unknown for this architecture"
|
|
||||||
# define __NR_seccomp 0xffff
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SECCOMP_SET_MODE_STRICT
|
|
||||||
#define SECCOMP_SET_MODE_STRICT 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SECCOMP_SET_MODE_FILTER
|
|
||||||
#define SECCOMP_SET_MODE_FILTER 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SECCOMP_FILTER_FLAG_TSYNC
|
|
||||||
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef seccomp
|
|
||||||
int seccomp(unsigned int op, unsigned int flags, void *args)
|
|
||||||
{
|
|
||||||
errno = 0;
|
|
||||||
return syscall(__NR_seccomp, op, flags, args);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(seccomp_syscall)
|
TEST(seccomp_syscall)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
struct sock_filter filter[] = {
|
||||||
|
@ -1783,6 +2045,67 @@ TEST(seccomp_syscall_mode_lock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test detection of known and unknown filter flags. Userspace needs to be able
|
||||||
|
* to check if a filter flag is supported by the current kernel and a good way
|
||||||
|
* of doing that is by attempting to enter filter mode, with the flag bit in
|
||||||
|
* question set, and a NULL pointer for the _args_ parameter. EFAULT indicates
|
||||||
|
* that the flag is valid and EINVAL indicates that the flag is invalid.
|
||||||
|
*/
|
||||||
|
TEST(detect_seccomp_filter_flags)
|
||||||
|
{
|
||||||
|
unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC,
|
||||||
|
SECCOMP_FILTER_FLAG_LOG };
|
||||||
|
unsigned int flag, all_flags;
|
||||||
|
int i;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
/* Test detection of known-good filter flags */
|
||||||
|
for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) {
|
||||||
|
flag = flags[i];
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
|
||||||
|
ASSERT_NE(ENOSYS, errno) {
|
||||||
|
TH_LOG("Kernel does not support seccomp syscall!");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(-1, ret);
|
||||||
|
EXPECT_EQ(EFAULT, errno) {
|
||||||
|
TH_LOG("Failed to detect that a known-good filter flag (0x%X) is supported!",
|
||||||
|
flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
all_flags |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test detection of all known-good filter flags */
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, all_flags, NULL);
|
||||||
|
EXPECT_EQ(-1, ret);
|
||||||
|
EXPECT_EQ(EFAULT, errno) {
|
||||||
|
TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!",
|
||||||
|
all_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test detection of an unknown filter flag */
|
||||||
|
flag = -1;
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
|
||||||
|
EXPECT_EQ(-1, ret);
|
||||||
|
EXPECT_EQ(EINVAL, errno) {
|
||||||
|
TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported!",
|
||||||
|
flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test detection of an unknown filter flag that may simply need to be
|
||||||
|
* added to this test
|
||||||
|
*/
|
||||||
|
flag = flags[ARRAY_SIZE(flags) - 1] << 1;
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL);
|
||||||
|
EXPECT_EQ(-1, ret);
|
||||||
|
EXPECT_EQ(EINVAL, errno) {
|
||||||
|
TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported! Does a new flag need to be added to this test?",
|
||||||
|
flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(TSYNC_first)
|
TEST(TSYNC_first)
|
||||||
{
|
{
|
||||||
struct sock_filter filter[] = {
|
struct sock_filter filter[] = {
|
||||||
|
@ -2421,6 +2744,99 @@ TEST(syscall_restart)
|
||||||
_metadata->passed = 0;
|
_metadata->passed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_SIGNAL(filter_flag_log, SIGSYS)
|
||||||
|
{
|
||||||
|
struct sock_filter allow_filter[] = {
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
};
|
||||||
|
struct sock_filter kill_filter[] = {
|
||||||
|
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
|
||||||
|
offsetof(struct seccomp_data, nr)),
|
||||||
|
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
|
||||||
|
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
|
||||||
|
};
|
||||||
|
struct sock_fprog allow_prog = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(allow_filter),
|
||||||
|
.filter = allow_filter,
|
||||||
|
};
|
||||||
|
struct sock_fprog kill_prog = {
|
||||||
|
.len = (unsigned short)ARRAY_SIZE(kill_filter),
|
||||||
|
.filter = kill_filter,
|
||||||
|
};
|
||||||
|
long ret;
|
||||||
|
pid_t parent = getppid();
|
||||||
|
|
||||||
|
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||||
|
ASSERT_EQ(0, ret);
|
||||||
|
|
||||||
|
/* Verify that the FILTER_FLAG_LOG flag isn't accepted in strict mode */
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_STRICT, SECCOMP_FILTER_FLAG_LOG,
|
||||||
|
&allow_prog);
|
||||||
|
ASSERT_NE(ENOSYS, errno) {
|
||||||
|
TH_LOG("Kernel does not support seccomp syscall!");
|
||||||
|
}
|
||||||
|
EXPECT_NE(0, ret) {
|
||||||
|
TH_LOG("Kernel accepted FILTER_FLAG_LOG flag in strict mode!");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(EINVAL, errno) {
|
||||||
|
TH_LOG("Kernel returned unexpected errno for FILTER_FLAG_LOG flag in strict mode!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify that a simple, permissive filter can be added with no flags */
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog);
|
||||||
|
EXPECT_EQ(0, ret);
|
||||||
|
|
||||||
|
/* See if the same filter can be added with the FILTER_FLAG_LOG flag */
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
|
||||||
|
&allow_prog);
|
||||||
|
ASSERT_NE(EINVAL, errno) {
|
||||||
|
TH_LOG("Kernel does not support the FILTER_FLAG_LOG flag!");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, ret);
|
||||||
|
|
||||||
|
/* Ensure that the kill filter works with the FILTER_FLAG_LOG flag */
|
||||||
|
ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG,
|
||||||
|
&kill_prog);
|
||||||
|
EXPECT_EQ(0, ret);
|
||||||
|
|
||||||
|
EXPECT_EQ(parent, syscall(__NR_getppid));
|
||||||
|
/* getpid() should never return. */
|
||||||
|
EXPECT_EQ(0, syscall(__NR_getpid));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(get_action_avail)
|
||||||
|
{
|
||||||
|
__u32 actions[] = { SECCOMP_RET_KILL_THREAD, SECCOMP_RET_TRAP,
|
||||||
|
SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE,
|
||||||
|
SECCOMP_RET_LOG, SECCOMP_RET_ALLOW };
|
||||||
|
__u32 unknown_action = 0x10000000U;
|
||||||
|
int i;
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[0]);
|
||||||
|
ASSERT_NE(ENOSYS, errno) {
|
||||||
|
TH_LOG("Kernel does not support seccomp syscall!");
|
||||||
|
}
|
||||||
|
ASSERT_NE(EINVAL, errno) {
|
||||||
|
TH_LOG("Kernel does not support SECCOMP_GET_ACTION_AVAIL operation!");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(ret, 0);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(actions); i++) {
|
||||||
|
ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[i]);
|
||||||
|
EXPECT_EQ(ret, 0) {
|
||||||
|
TH_LOG("Expected action (0x%X) not available!",
|
||||||
|
actions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that an unknown action is handled properly (EOPNOTSUPP) */
|
||||||
|
ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &unknown_action);
|
||||||
|
EXPECT_EQ(ret, -1);
|
||||||
|
EXPECT_EQ(errno, EOPNOTSUPP);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
* - add microbenchmarks
|
* - add microbenchmarks
|
||||||
|
@ -2429,6 +2845,8 @@ TEST(syscall_restart)
|
||||||
* - endianness checking when appropriate
|
* - endianness checking when appropriate
|
||||||
* - 64-bit arg prodding
|
* - 64-bit arg prodding
|
||||||
* - arch value testing (x86 modes especially)
|
* - arch value testing (x86 modes especially)
|
||||||
|
* - verify that FILTER_FLAG_LOG filters generate log messages
|
||||||
|
* - verify that RET_LOG generates log messages
|
||||||
* - ...
|
* - ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue