mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-05 19:31:57 -04:00
Merge remote-tracking branch 'sm8350/lineage-20' into lineage-23.2
* sm8350/lineage-20: (306 commits) ANDROID: gki_defconfig: reduce KFENCE pool size ANDROID: GKI: enable KFENCE by setting the sample interval to 500ms ANDROID: GKI: Enable KFENCE UPSTREAM: random: split initialization into early step and later step UPSTREAM: kfence: avoid passing -g for test UPSTREAM: net: add and use skb_unclone_keeptruesize() helper UPSTREAM: kfence: fix memory leak when cat kfence objects UPSTREAM: kfence: unconditionally use unbound work queue UPSTREAM: kfence: use TASK_IDLE when awaiting allocation UPSTREAM: kfence: fix is_kfence_address() for addresses below KFENCE_POOL_SIZE FROMLIST: kfence: skip all GFP_ZONEMASK allocations FROMLIST: kfence: move the size check to the beginning of __kfence_alloc() ANDROID: kasan: fix interoperability with KFENCE UPSTREAM: arm64: mm: don't use CON and BLK mapping if KFENCE is enabled ANDROID: kfence: clean up unused variables FROMGIT: kfence: use power-efficient work queue to run delayed work FROMGIT: kfence: maximize allocation wait timeout duration FROMGIT: kfence: await for allocation using wait_event FROMGIT: kfence: zero guard page after out-of-bounds access UPSTREAM: kfence: make compatible with kmemleak ... Change-Id: Id1436b77692839dc2394f500bba42487fac1b2b4
This commit is contained in:
commit
376925d57f
209 changed files with 10117 additions and 1291 deletions
|
|
@ -274,6 +274,7 @@ repeat:
|
|||
}
|
||||
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
seccomp_filter_release(p);
|
||||
release_thread(p);
|
||||
put_task_struct_rcu_user(p);
|
||||
|
||||
|
|
|
|||
|
|
@ -474,7 +474,6 @@ void free_task(struct task_struct *tsk)
|
|||
#endif
|
||||
rt_mutex_debug_task_free(tsk);
|
||||
ftrace_graph_exit_task(tsk);
|
||||
put_seccomp_filter(tsk);
|
||||
arch_release_task_struct(tsk);
|
||||
if (tsk->flags & PF_KTHREAD)
|
||||
free_kthread_struct(tsk);
|
||||
|
|
|
|||
509
kernel/seccomp.c
509
kernel/seccomp.c
|
|
@ -13,6 +13,7 @@
|
|||
* Mode 2 allows user-defined system call filters in the form
|
||||
* of Berkeley Packet Filters/Linux Socket Filters.
|
||||
*/
|
||||
#define pr_fmt(fmt) "seccomp: " fmt
|
||||
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/audit.h>
|
||||
|
|
@ -44,6 +45,7 @@
|
|||
#include <linux/tracehook.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
/*
|
||||
* When SECCOMP_IOCTL_NOTIF_ID_VALID was first introduced, it had the
|
||||
|
|
@ -86,6 +88,7 @@ struct seccomp_knotif {
|
|||
/* The return values, only valid when in SECCOMP_NOTIFY_REPLIED */
|
||||
int error;
|
||||
long val;
|
||||
u32 flags;
|
||||
|
||||
/* Signals when this has entered SECCOMP_NOTIFY_REPLIED */
|
||||
struct completion ready;
|
||||
|
|
@ -104,27 +107,68 @@ struct seccomp_knotif {
|
|||
* filter->notify_lock.
|
||||
* @next_id: The id of the next request.
|
||||
* @notifications: A list of struct seccomp_knotif elements.
|
||||
* @wqh: A wait queue for poll.
|
||||
*/
|
||||
struct notification {
|
||||
struct semaphore request;
|
||||
u64 next_id;
|
||||
struct list_head notifications;
|
||||
wait_queue_head_t wqh;
|
||||
};
|
||||
|
||||
#ifdef SECCOMP_ARCH_NATIVE
|
||||
/**
|
||||
* struct action_cache - per-filter cache of seccomp actions per
|
||||
* arch/syscall pair
|
||||
*
|
||||
* @allow_native: A bitmap where each bit represents whether the
|
||||
* filter will always allow the syscall, for the
|
||||
* native architecture.
|
||||
* @allow_compat: A bitmap where each bit represents whether the
|
||||
* filter will always allow the syscall, for the
|
||||
* compat architecture.
|
||||
*/
|
||||
struct action_cache {
|
||||
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
||||
#ifdef SECCOMP_ARCH_COMPAT
|
||||
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
||||
#endif
|
||||
};
|
||||
#else
|
||||
struct action_cache { };
|
||||
|
||||
static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter,
|
||||
const struct seccomp_data *sd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void seccomp_cache_prepare(struct seccomp_filter *sfilter)
|
||||
{
|
||||
}
|
||||
#endif /* SECCOMP_ARCH_NATIVE */
|
||||
|
||||
/**
|
||||
* struct seccomp_filter - container for seccomp BPF programs
|
||||
*
|
||||
* @usage: reference count to manage the object lifetime.
|
||||
* get/put helpers should be used when accessing an instance
|
||||
* outside of a lifetime-guarded section. In general, this
|
||||
* is only needed for handling filters shared across tasks.
|
||||
* @refs: Reference count to manage the object lifetime.
|
||||
* A filter's reference count is incremented for each directly
|
||||
* attached task, once for the dependent filter, and if
|
||||
* requested for the user notifier. When @refs reaches zero,
|
||||
* the filter can be freed.
|
||||
* @users: A filter's @users count is incremented for each directly
|
||||
* attached task (filter installation, fork(), thread_sync),
|
||||
* and once for the dependent filter (tracked in filter->prev).
|
||||
* When it reaches zero it indicates that no direct or indirect
|
||||
* users of that filter exist. No new tasks can get associated with
|
||||
* this filter after reaching 0. The @users count is always smaller
|
||||
* or equal to @refs. Hence, reaching 0 for @users does not mean
|
||||
* the filter can be freed.
|
||||
* @cache: cache of arch/syscall mappings to actions
|
||||
* @log: true if all actions except for SECCOMP_RET_ALLOW should be logged
|
||||
* @prev: points to a previously installed, or inherited, filter
|
||||
* @prog: the BPF program to evaluate
|
||||
* @notif: the struct that holds all notification related information
|
||||
* @notify_lock: A lock for all notification-related accesses.
|
||||
* @wqh: A wait queue for poll if a notifier is in use.
|
||||
*
|
||||
* seccomp_filter objects are organized in a tree linked via the @prev
|
||||
* pointer. For any task, it appears to be a singly-linked list starting
|
||||
|
|
@ -134,15 +178,18 @@ struct notification {
|
|||
* how namespaces work.
|
||||
*
|
||||
* seccomp_filter objects should never be modified after being attached
|
||||
* to a task_struct (other than @usage).
|
||||
* to a task_struct (other than @refs).
|
||||
*/
|
||||
struct seccomp_filter {
|
||||
refcount_t usage;
|
||||
refcount_t refs;
|
||||
refcount_t users;
|
||||
bool log;
|
||||
struct action_cache cache;
|
||||
struct seccomp_filter *prev;
|
||||
struct bpf_prog *prog;
|
||||
struct notification *notif;
|
||||
struct mutex notify_lock;
|
||||
wait_queue_head_t wqh;
|
||||
};
|
||||
|
||||
/* Limit any path through the tree to 256KB worth of instructions. */
|
||||
|
|
@ -154,6 +201,10 @@ struct seccomp_filter {
|
|||
*/
|
||||
static void populate_seccomp_data(struct seccomp_data *sd)
|
||||
{
|
||||
/*
|
||||
* Instead of using current_pt_reg(), we're already doing the work
|
||||
* to safely fetch "current", so just use "task" everywhere below.
|
||||
*/
|
||||
struct task_struct *task = current;
|
||||
struct pt_regs *regs = task_pt_regs(task);
|
||||
unsigned long args[6];
|
||||
|
|
@ -252,6 +303,52 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SECCOMP_ARCH_NATIVE
|
||||
static inline bool seccomp_cache_check_allow_bitmap(const void *bitmap,
|
||||
size_t bitmap_size,
|
||||
int syscall_nr)
|
||||
{
|
||||
if (unlikely(syscall_nr < 0 || syscall_nr >= bitmap_size))
|
||||
return false;
|
||||
syscall_nr = array_index_nospec(syscall_nr, bitmap_size);
|
||||
|
||||
return test_bit(syscall_nr, bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_cache_check_allow - lookup seccomp cache
|
||||
* @sfilter: The seccomp filter
|
||||
* @sd: The seccomp data to lookup the cache with
|
||||
*
|
||||
* Returns true if the seccomp_data is cached and allowed.
|
||||
*/
|
||||
static inline bool seccomp_cache_check_allow(const struct seccomp_filter *sfilter,
|
||||
const struct seccomp_data *sd)
|
||||
{
|
||||
int syscall_nr = sd->nr;
|
||||
const struct action_cache *cache = &sfilter->cache;
|
||||
|
||||
#ifndef SECCOMP_ARCH_COMPAT
|
||||
/* A native-only architecture doesn't need to check sd->arch. */
|
||||
return seccomp_cache_check_allow_bitmap(cache->allow_native,
|
||||
SECCOMP_ARCH_NATIVE_NR,
|
||||
syscall_nr);
|
||||
#else
|
||||
if (likely(sd->arch == SECCOMP_ARCH_NATIVE))
|
||||
return seccomp_cache_check_allow_bitmap(cache->allow_native,
|
||||
SECCOMP_ARCH_NATIVE_NR,
|
||||
syscall_nr);
|
||||
if (likely(sd->arch == SECCOMP_ARCH_COMPAT))
|
||||
return seccomp_cache_check_allow_bitmap(cache->allow_compat,
|
||||
SECCOMP_ARCH_COMPAT_NR,
|
||||
syscall_nr);
|
||||
#endif /* SECCOMP_ARCH_COMPAT */
|
||||
|
||||
WARN_ON_ONCE(true);
|
||||
return false;
|
||||
}
|
||||
#endif /* SECCOMP_ARCH_NATIVE */
|
||||
|
||||
/**
|
||||
* seccomp_run_filters - evaluates all seccomp filters against @sd
|
||||
* @sd: optional seccomp data to be passed to filters
|
||||
|
|
@ -274,6 +371,9 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd,
|
|||
if (WARN_ON(f == NULL))
|
||||
return SECCOMP_RET_KILL_PROCESS;
|
||||
|
||||
if (seccomp_cache_check_allow(f, sd))
|
||||
return SECCOMP_RET_ALLOW;
|
||||
|
||||
/*
|
||||
* All filters in the list are evaluated and the lowest BPF return
|
||||
* value always takes priority (ignoring the DATA).
|
||||
|
|
@ -378,6 +478,59 @@ static inline pid_t seccomp_can_sync_threads(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void seccomp_filter_free(struct seccomp_filter *filter)
|
||||
{
|
||||
if (filter) {
|
||||
bpf_prog_destroy(filter->prog);
|
||||
kfree(filter);
|
||||
}
|
||||
}
|
||||
|
||||
static void __seccomp_filter_orphan(struct seccomp_filter *orig)
|
||||
{
|
||||
while (orig && refcount_dec_and_test(&orig->users)) {
|
||||
if (waitqueue_active(&orig->wqh))
|
||||
wake_up_poll(&orig->wqh, EPOLLHUP);
|
||||
orig = orig->prev;
|
||||
}
|
||||
}
|
||||
|
||||
static void __put_seccomp_filter(struct seccomp_filter *orig)
|
||||
{
|
||||
/* Clean up single-reference branches iteratively. */
|
||||
while (orig && refcount_dec_and_test(&orig->refs)) {
|
||||
struct seccomp_filter *freeme = orig;
|
||||
orig = orig->prev;
|
||||
seccomp_filter_free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
static void __seccomp_filter_release(struct seccomp_filter *orig)
|
||||
{
|
||||
/* Notify about any unused filters in the task's former filter tree. */
|
||||
__seccomp_filter_orphan(orig);
|
||||
/* Finally drop all references to the task's former tree. */
|
||||
__put_seccomp_filter(orig);
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_filter_release - Detach the task from its filter tree,
|
||||
* drop its reference count, and notify
|
||||
* about unused filters
|
||||
*
|
||||
* This function should only be called when the task is exiting as
|
||||
* it detaches it from its filter tree. As such, READ_ONCE() and
|
||||
* barriers are not needed here, as would normally be needed.
|
||||
*/
|
||||
void seccomp_filter_release(struct task_struct *tsk)
|
||||
{
|
||||
struct seccomp_filter *orig = tsk->seccomp.filter;
|
||||
|
||||
/* Detach task from its filter tree. */
|
||||
tsk->seccomp.filter = NULL;
|
||||
__seccomp_filter_release(orig);
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_sync_threads: sets all threads to use current's filter
|
||||
*
|
||||
|
|
@ -402,14 +555,19 @@ static inline void seccomp_sync_threads(unsigned long flags)
|
|||
|
||||
/* Get a task reference for the new leaf node. */
|
||||
get_seccomp_filter(caller);
|
||||
|
||||
/*
|
||||
* Drop the task reference to the shared ancestor since
|
||||
* current's path will hold a reference. (This also
|
||||
* allows a put before the assignment.)
|
||||
*/
|
||||
put_seccomp_filter(thread);
|
||||
__seccomp_filter_release(thread->seccomp.filter);
|
||||
|
||||
/* Make our new filter tree visible. */
|
||||
smp_store_release(&thread->seccomp.filter,
|
||||
caller->seccomp.filter);
|
||||
atomic_set(&thread->seccomp.filter_count,
|
||||
atomic_read(&caller->seccomp.filter_count));
|
||||
|
||||
/*
|
||||
* Don't let an unprivileged task work around
|
||||
|
|
@ -442,7 +600,12 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
|||
{
|
||||
struct seccomp_filter *sfilter;
|
||||
int ret;
|
||||
const bool save_orig = IS_ENABLED(CONFIG_CHECKPOINT_RESTORE);
|
||||
const bool save_orig =
|
||||
#if defined(CONFIG_CHECKPOINT_RESTORE) || defined(SECCOMP_ARCH_NATIVE)
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
|
@ -472,7 +635,9 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
refcount_set(&sfilter->usage, 1);
|
||||
refcount_set(&sfilter->refs, 1);
|
||||
refcount_set(&sfilter->users, 1);
|
||||
init_waitqueue_head(&sfilter->wqh);
|
||||
|
||||
return sfilter;
|
||||
}
|
||||
|
|
@ -505,6 +670,148 @@ out:
|
|||
return filter;
|
||||
}
|
||||
|
||||
#ifdef SECCOMP_ARCH_NATIVE
|
||||
/**
|
||||
* seccomp_is_const_allow - check if filter is constant allow with given data
|
||||
* @fprog: The BPF programs
|
||||
* @sd: The seccomp data to check against, only syscall number and arch
|
||||
* number are considered constant.
|
||||
*/
|
||||
static bool seccomp_is_const_allow(struct sock_fprog_kern *fprog,
|
||||
struct seccomp_data *sd)
|
||||
{
|
||||
unsigned int reg_value = 0;
|
||||
unsigned int pc;
|
||||
bool op_res;
|
||||
|
||||
if (WARN_ON_ONCE(!fprog))
|
||||
return false;
|
||||
|
||||
for (pc = 0; pc < fprog->len; pc++) {
|
||||
struct sock_filter *insn = &fprog->filter[pc];
|
||||
u16 code = insn->code;
|
||||
u32 k = insn->k;
|
||||
|
||||
switch (code) {
|
||||
case BPF_LD | BPF_W | BPF_ABS:
|
||||
switch (k) {
|
||||
case offsetof(struct seccomp_data, nr):
|
||||
reg_value = sd->nr;
|
||||
break;
|
||||
case offsetof(struct seccomp_data, arch):
|
||||
reg_value = sd->arch;
|
||||
break;
|
||||
default:
|
||||
/* can't optimize (non-constant value load) */
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case BPF_RET | BPF_K:
|
||||
/* reached return with constant values only, check allow */
|
||||
return k == SECCOMP_RET_ALLOW;
|
||||
case BPF_JMP | BPF_JA:
|
||||
pc += insn->k;
|
||||
break;
|
||||
case BPF_JMP | BPF_JEQ | BPF_K:
|
||||
case BPF_JMP | BPF_JGE | BPF_K:
|
||||
case BPF_JMP | BPF_JGT | BPF_K:
|
||||
case BPF_JMP | BPF_JSET | BPF_K:
|
||||
switch (BPF_OP(code)) {
|
||||
case BPF_JEQ:
|
||||
op_res = reg_value == k;
|
||||
break;
|
||||
case BPF_JGE:
|
||||
op_res = reg_value >= k;
|
||||
break;
|
||||
case BPF_JGT:
|
||||
op_res = reg_value > k;
|
||||
break;
|
||||
case BPF_JSET:
|
||||
op_res = !!(reg_value & k);
|
||||
break;
|
||||
default:
|
||||
/* can't optimize (unknown jump) */
|
||||
return false;
|
||||
}
|
||||
|
||||
pc += op_res ? insn->jt : insn->jf;
|
||||
break;
|
||||
case BPF_ALU | BPF_AND | BPF_K:
|
||||
reg_value &= k;
|
||||
break;
|
||||
default:
|
||||
/* can't optimize (unknown insn) */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ran off the end of the filter?! */
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void seccomp_cache_prepare_bitmap(struct seccomp_filter *sfilter,
|
||||
void *bitmap, const void *bitmap_prev,
|
||||
size_t bitmap_size, int arch)
|
||||
{
|
||||
struct sock_fprog_kern *fprog = sfilter->prog->orig_prog;
|
||||
struct seccomp_data sd;
|
||||
int nr;
|
||||
|
||||
if (bitmap_prev) {
|
||||
/* The new filter must be as restrictive as the last. */
|
||||
bitmap_copy(bitmap, bitmap_prev, bitmap_size);
|
||||
} else {
|
||||
/* Before any filters, all syscalls are always allowed. */
|
||||
bitmap_fill(bitmap, bitmap_size);
|
||||
}
|
||||
|
||||
for (nr = 0; nr < bitmap_size; nr++) {
|
||||
/* No bitmap change: not a cacheable action. */
|
||||
if (!test_bit(nr, bitmap))
|
||||
continue;
|
||||
|
||||
sd.nr = nr;
|
||||
sd.arch = arch;
|
||||
|
||||
/* No bitmap change: continue to always allow. */
|
||||
if (seccomp_is_const_allow(fprog, &sd))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Not a cacheable action: always run filters.
|
||||
* atomic clear_bit() not needed, filter not visible yet.
|
||||
*/
|
||||
__clear_bit(nr, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* seccomp_cache_prepare - emulate the filter to find cachable syscalls
|
||||
* @sfilter: The seccomp filter
|
||||
*
|
||||
* Returns 0 if successful or -errno if error occurred.
|
||||
*/
|
||||
static void seccomp_cache_prepare(struct seccomp_filter *sfilter)
|
||||
{
|
||||
struct action_cache *cache = &sfilter->cache;
|
||||
const struct action_cache *cache_prev =
|
||||
sfilter->prev ? &sfilter->prev->cache : NULL;
|
||||
|
||||
seccomp_cache_prepare_bitmap(sfilter, cache->allow_native,
|
||||
cache_prev ? cache_prev->allow_native : NULL,
|
||||
SECCOMP_ARCH_NATIVE_NR,
|
||||
SECCOMP_ARCH_NATIVE);
|
||||
|
||||
#ifdef SECCOMP_ARCH_COMPAT
|
||||
seccomp_cache_prepare_bitmap(sfilter, cache->allow_compat,
|
||||
cache_prev ? cache_prev->allow_compat : NULL,
|
||||
SECCOMP_ARCH_COMPAT_NR,
|
||||
SECCOMP_ARCH_COMPAT);
|
||||
#endif /* SECCOMP_ARCH_COMPAT */
|
||||
}
|
||||
#endif /* SECCOMP_ARCH_NATIVE */
|
||||
|
||||
/**
|
||||
* seccomp_attach_filter: validate and attach filter
|
||||
* @flags: flags to change filter behavior
|
||||
|
|
@ -550,7 +857,9 @@ static long seccomp_attach_filter(unsigned int flags,
|
|||
* task reference.
|
||||
*/
|
||||
filter->prev = current->seccomp.filter;
|
||||
seccomp_cache_prepare(filter);
|
||||
current->seccomp.filter = filter;
|
||||
atomic_inc(¤t->seccomp.filter_count);
|
||||
|
||||
/* Now that the new filter is in place, synchronize to all threads. */
|
||||
if (flags & SECCOMP_FILTER_FLAG_TSYNC)
|
||||
|
|
@ -561,7 +870,7 @@ static long seccomp_attach_filter(unsigned int flags,
|
|||
|
||||
static void __get_seccomp_filter(struct seccomp_filter *filter)
|
||||
{
|
||||
refcount_inc(&filter->usage);
|
||||
refcount_inc(&filter->refs);
|
||||
}
|
||||
|
||||
/* get_seccomp_filter - increments the reference count of the filter on @tsk */
|
||||
|
|
@ -571,30 +880,7 @@ void get_seccomp_filter(struct task_struct *tsk)
|
|||
if (!orig)
|
||||
return;
|
||||
__get_seccomp_filter(orig);
|
||||
}
|
||||
|
||||
static inline void seccomp_filter_free(struct seccomp_filter *filter)
|
||||
{
|
||||
if (filter) {
|
||||
bpf_prog_destroy(filter->prog);
|
||||
kfree(filter);
|
||||
}
|
||||
}
|
||||
|
||||
static void __put_seccomp_filter(struct seccomp_filter *orig)
|
||||
{
|
||||
/* Clean up single-reference branches iteratively. */
|
||||
while (orig && refcount_dec_and_test(&orig->usage)) {
|
||||
struct seccomp_filter *freeme = orig;
|
||||
orig = orig->prev;
|
||||
seccomp_filter_free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
|
||||
void put_seccomp_filter(struct task_struct *tsk)
|
||||
{
|
||||
__put_seccomp_filter(tsk->seccomp.filter);
|
||||
refcount_inc(&orig->users);
|
||||
}
|
||||
|
||||
static void seccomp_init_siginfo(kernel_siginfo_t *info, int syscall, int reason)
|
||||
|
|
@ -691,20 +977,20 @@ static inline void seccomp_log(unsigned long syscall, long signr, u32 action,
|
|||
*/
|
||||
static const int mode1_syscalls[] = {
|
||||
__NR_seccomp_read, __NR_seccomp_write, __NR_seccomp_exit, __NR_seccomp_sigreturn,
|
||||
0, /* null terminated */
|
||||
-1, /* negative terminated */
|
||||
};
|
||||
|
||||
static void __secure_computing_strict(int this_syscall)
|
||||
{
|
||||
const int *syscall_whitelist = mode1_syscalls;
|
||||
const int *allowed_syscalls = mode1_syscalls;
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (in_compat_syscall())
|
||||
syscall_whitelist = get_compat_mode1_syscalls();
|
||||
allowed_syscalls = get_compat_mode1_syscalls();
|
||||
#endif
|
||||
do {
|
||||
if (*syscall_whitelist == this_syscall)
|
||||
if (*allowed_syscalls == this_syscall)
|
||||
return;
|
||||
} while (*++syscall_whitelist);
|
||||
} while (*++allowed_syscalls != -1);
|
||||
|
||||
#ifdef SECCOMP_DEBUG
|
||||
dump_stack();
|
||||
|
|
@ -743,11 +1029,12 @@ static u64 seccomp_next_notify_id(struct seccomp_filter *filter)
|
|||
return filter->notif->next_id++;
|
||||
}
|
||||
|
||||
static void seccomp_do_user_notification(int this_syscall,
|
||||
struct seccomp_filter *match,
|
||||
const struct seccomp_data *sd)
|
||||
static int seccomp_do_user_notification(int this_syscall,
|
||||
struct seccomp_filter *match,
|
||||
const struct seccomp_data *sd)
|
||||
{
|
||||
int err;
|
||||
u32 flags = 0;
|
||||
long ret = 0;
|
||||
struct seccomp_knotif n = {};
|
||||
|
||||
|
|
@ -764,7 +1051,7 @@ static void seccomp_do_user_notification(int this_syscall,
|
|||
list_add(&n.list, &match->notif->notifications);
|
||||
|
||||
up(&match->notif->request);
|
||||
wake_up_poll(&match->notif->wqh, EPOLLIN | EPOLLRDNORM);
|
||||
wake_up_poll(&match->wqh, EPOLLIN | EPOLLRDNORM);
|
||||
mutex_unlock(&match->notify_lock);
|
||||
|
||||
/*
|
||||
|
|
@ -775,6 +1062,7 @@ static void seccomp_do_user_notification(int this_syscall,
|
|||
if (err == 0) {
|
||||
ret = n.val;
|
||||
err = n.error;
|
||||
flags = n.flags;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -791,8 +1079,14 @@ static void seccomp_do_user_notification(int this_syscall,
|
|||
list_del(&n.list);
|
||||
out:
|
||||
mutex_unlock(&match->notify_lock);
|
||||
syscall_set_return_value(current, task_pt_regs(current),
|
||||
|
||||
/* Userspace requests to continue the syscall. */
|
||||
if (flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE)
|
||||
return 0;
|
||||
|
||||
syscall_set_return_value(current, current_pt_regs(),
|
||||
err, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
||||
|
|
@ -823,13 +1117,13 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
|||
/* Set low-order bits as an errno, capped at MAX_ERRNO. */
|
||||
if (data > MAX_ERRNO)
|
||||
data = MAX_ERRNO;
|
||||
syscall_set_return_value(current, task_pt_regs(current),
|
||||
syscall_set_return_value(current, current_pt_regs(),
|
||||
-data, 0);
|
||||
goto skip;
|
||||
|
||||
case SECCOMP_RET_TRAP:
|
||||
/* Show the handler the original registers. */
|
||||
syscall_rollback(current, task_pt_regs(current));
|
||||
syscall_rollback(current, current_pt_regs());
|
||||
/* Let the filter pass back 16 bits of data. */
|
||||
seccomp_send_sigsys(this_syscall, data);
|
||||
goto skip;
|
||||
|
|
@ -842,7 +1136,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
|||
/* ENOSYS these calls if there is no tracer attached. */
|
||||
if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) {
|
||||
syscall_set_return_value(current,
|
||||
task_pt_regs(current),
|
||||
current_pt_regs(),
|
||||
-ENOSYS, 0);
|
||||
goto skip;
|
||||
}
|
||||
|
|
@ -862,7 +1156,7 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
|||
if (fatal_signal_pending(current))
|
||||
goto skip;
|
||||
/* Check if the tracer forced the syscall to be skipped. */
|
||||
this_syscall = syscall_get_nr(current, task_pt_regs(current));
|
||||
this_syscall = syscall_get_nr(current, current_pt_regs());
|
||||
if (this_syscall < 0)
|
||||
goto skip;
|
||||
|
||||
|
|
@ -878,8 +1172,10 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
|||
return 0;
|
||||
|
||||
case SECCOMP_RET_USER_NOTIF:
|
||||
seccomp_do_user_notification(this_syscall, match, sd);
|
||||
goto skip;
|
||||
if (seccomp_do_user_notification(this_syscall, match, sd))
|
||||
goto skip;
|
||||
|
||||
return 0;
|
||||
|
||||
case SECCOMP_RET_LOG:
|
||||
seccomp_log(this_syscall, 0, action, true);
|
||||
|
|
@ -899,20 +1195,20 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd,
|
|||
current->seccomp.mode = SECCOMP_MODE_DEAD;
|
||||
seccomp_log(this_syscall, SIGSYS, action, true);
|
||||
/* Dump core only if this is the last remaining thread. */
|
||||
if (action == SECCOMP_RET_KILL_PROCESS ||
|
||||
if (action != SECCOMP_RET_KILL_THREAD ||
|
||||
get_nr_threads(current) == 1) {
|
||||
kernel_siginfo_t info;
|
||||
|
||||
/* Show the original registers in the dump. */
|
||||
syscall_rollback(current, task_pt_regs(current));
|
||||
syscall_rollback(current, current_pt_regs());
|
||||
/* Trigger a manual coredump since do_exit skips it. */
|
||||
seccomp_init_siginfo(&info, this_syscall, data);
|
||||
do_coredump(&info);
|
||||
}
|
||||
if (action == SECCOMP_RET_KILL_PROCESS)
|
||||
do_group_exit(SIGSYS);
|
||||
else
|
||||
if (action == SECCOMP_RET_KILL_THREAD)
|
||||
do_exit(SIGSYS);
|
||||
else
|
||||
do_group_exit(SIGSYS);
|
||||
}
|
||||
|
||||
unreachable();
|
||||
|
|
@ -941,7 +1237,7 @@ int __secure_computing(const struct seccomp_data *sd)
|
|||
return 0;
|
||||
|
||||
this_syscall = sd ? sd->nr :
|
||||
syscall_get_nr(current, task_pt_regs(current));
|
||||
syscall_get_nr(current, current_pt_regs());
|
||||
|
||||
switch (mode) {
|
||||
case SECCOMP_MODE_STRICT:
|
||||
|
|
@ -995,13 +1291,18 @@ out:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_SECCOMP_FILTER
|
||||
static int seccomp_notify_release(struct inode *inode, struct file *file)
|
||||
static void seccomp_notify_free(struct seccomp_filter *filter)
|
||||
{
|
||||
kfree(filter->notif);
|
||||
filter->notif = NULL;
|
||||
}
|
||||
|
||||
static void seccomp_notify_detach(struct seccomp_filter *filter)
|
||||
{
|
||||
struct seccomp_filter *filter = file->private_data;
|
||||
struct seccomp_knotif *knotif;
|
||||
|
||||
if (!filter)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
mutex_lock(&filter->notify_lock);
|
||||
|
||||
|
|
@ -1020,13 +1321,36 @@ static int seccomp_notify_release(struct inode *inode, struct file *file)
|
|||
complete(&knotif->ready);
|
||||
}
|
||||
|
||||
kfree(filter->notif);
|
||||
filter->notif = NULL;
|
||||
seccomp_notify_free(filter);
|
||||
mutex_unlock(&filter->notify_lock);
|
||||
}
|
||||
|
||||
static int seccomp_notify_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seccomp_filter *filter = file->private_data;
|
||||
|
||||
seccomp_notify_detach(filter);
|
||||
__put_seccomp_filter(filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must be called with notif_lock held */
|
||||
static inline struct seccomp_knotif *
|
||||
find_notification(struct seccomp_filter *filter, u64 id)
|
||||
{
|
||||
struct seccomp_knotif *cur;
|
||||
|
||||
lockdep_assert_held(&filter->notify_lock);
|
||||
|
||||
list_for_each_entry(cur, &filter->notif->notifications, list) {
|
||||
if (cur->id == id)
|
||||
return cur;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static long seccomp_notify_recv(struct seccomp_filter *filter,
|
||||
void __user *buf)
|
||||
{
|
||||
|
|
@ -1070,7 +1394,7 @@ static long seccomp_notify_recv(struct seccomp_filter *filter,
|
|||
unotif.data = *(knotif->data);
|
||||
|
||||
knotif->state = SECCOMP_NOTIFY_SENT;
|
||||
wake_up_poll(&filter->notif->wqh, EPOLLOUT | EPOLLWRNORM);
|
||||
wake_up_poll(&filter->wqh, EPOLLOUT | EPOLLWRNORM);
|
||||
ret = 0;
|
||||
out:
|
||||
mutex_unlock(&filter->notify_lock);
|
||||
|
|
@ -1084,15 +1408,8 @@ out:
|
|||
* may have died when we released the lock, so we need to make
|
||||
* sure it's still around.
|
||||
*/
|
||||
knotif = NULL;
|
||||
mutex_lock(&filter->notify_lock);
|
||||
list_for_each_entry(cur, &filter->notif->notifications, list) {
|
||||
if (cur->id == unotif.id) {
|
||||
knotif = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
knotif = find_notification(filter, unotif.id);
|
||||
if (knotif) {
|
||||
knotif->state = SECCOMP_NOTIFY_INIT;
|
||||
up(&filter->notif->request);
|
||||
|
|
@ -1107,26 +1424,24 @@ static long seccomp_notify_send(struct seccomp_filter *filter,
|
|||
void __user *buf)
|
||||
{
|
||||
struct seccomp_notif_resp resp = {};
|
||||
struct seccomp_knotif *knotif = NULL, *cur;
|
||||
struct seccomp_knotif *knotif;
|
||||
long ret;
|
||||
|
||||
if (copy_from_user(&resp, buf, sizeof(resp)))
|
||||
return -EFAULT;
|
||||
|
||||
if (resp.flags)
|
||||
if (resp.flags & ~SECCOMP_USER_NOTIF_FLAG_CONTINUE)
|
||||
return -EINVAL;
|
||||
|
||||
if ((resp.flags & SECCOMP_USER_NOTIF_FLAG_CONTINUE) &&
|
||||
(resp.error || resp.val))
|
||||
return -EINVAL;
|
||||
|
||||
ret = mutex_lock_interruptible(&filter->notify_lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(cur, &filter->notif->notifications, list) {
|
||||
if (cur->id == resp.id) {
|
||||
knotif = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
knotif = find_notification(filter, resp.id);
|
||||
if (!knotif) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
|
|
@ -1142,6 +1457,7 @@ static long seccomp_notify_send(struct seccomp_filter *filter,
|
|||
knotif->state = SECCOMP_NOTIFY_REPLIED;
|
||||
knotif->error = resp.error;
|
||||
knotif->val = resp.val;
|
||||
knotif->flags = resp.flags;
|
||||
complete(&knotif->ready);
|
||||
out:
|
||||
mutex_unlock(&filter->notify_lock);
|
||||
|
|
@ -1151,7 +1467,7 @@ out:
|
|||
static long seccomp_notify_id_valid(struct seccomp_filter *filter,
|
||||
void __user *buf)
|
||||
{
|
||||
struct seccomp_knotif *knotif = NULL;
|
||||
struct seccomp_knotif *knotif;
|
||||
u64 id;
|
||||
long ret;
|
||||
|
||||
|
|
@ -1162,16 +1478,12 @@ static long seccomp_notify_id_valid(struct seccomp_filter *filter,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = -ENOENT;
|
||||
list_for_each_entry(knotif, &filter->notif->notifications, list) {
|
||||
if (knotif->id == id) {
|
||||
if (knotif->state == SECCOMP_NOTIFY_SENT)
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
knotif = find_notification(filter, id);
|
||||
if (knotif && knotif->state == SECCOMP_NOTIFY_SENT)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -ENOENT;
|
||||
|
||||
out:
|
||||
mutex_unlock(&filter->notify_lock);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1202,7 +1514,7 @@ static __poll_t seccomp_notify_poll(struct file *file,
|
|||
__poll_t ret = 0;
|
||||
struct seccomp_knotif *cur;
|
||||
|
||||
poll_wait(file, &filter->notif->wqh, poll_tab);
|
||||
poll_wait(file, &filter->wqh, poll_tab);
|
||||
|
||||
if (mutex_lock_interruptible(&filter->notify_lock) < 0)
|
||||
return EPOLLERR;
|
||||
|
|
@ -1218,6 +1530,9 @@ static __poll_t seccomp_notify_poll(struct file *file,
|
|||
|
||||
mutex_unlock(&filter->notify_lock);
|
||||
|
||||
if (refcount_read(&filter->users) == 0)
|
||||
ret |= EPOLLHUP;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1240,7 +1555,6 @@ static struct file *init_listener(struct seccomp_filter *filter)
|
|||
sema_init(&filter->notif->request, 0);
|
||||
filter->notif->next_id = get_random_u64();
|
||||
INIT_LIST_HEAD(&filter->notif->notifications);
|
||||
init_waitqueue_head(&filter->notif->wqh);
|
||||
|
||||
ret = anon_inode_getfile("seccomp notify", &seccomp_notify_ops,
|
||||
filter, O_RDWR);
|
||||
|
|
@ -1252,7 +1566,7 @@ static struct file *init_listener(struct seccomp_filter *filter)
|
|||
|
||||
out_notif:
|
||||
if (IS_ERR(ret))
|
||||
kfree(filter->notif);
|
||||
seccomp_notify_free(filter);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1373,6 +1687,7 @@ out_put_fd:
|
|||
listener_f->private_data = NULL;
|
||||
fput(listener_f);
|
||||
put_unused_fd(listener);
|
||||
seccomp_notify_detach(prepared);
|
||||
} else {
|
||||
fd_install(listener, listener_f);
|
||||
ret = listener;
|
||||
|
|
@ -1720,7 +2035,7 @@ static bool seccomp_actions_logged_from_names(u32 *actions_logged, char *names)
|
|||
return true;
|
||||
}
|
||||
|
||||
static int read_actions_logged(struct ctl_table *ro_table, void *buffer,
|
||||
static int read_actions_logged(struct ctl_table *ro_table, void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
char names[sizeof(seccomp_actions_avail)];
|
||||
|
|
@ -1738,7 +2053,7 @@ static int read_actions_logged(struct ctl_table *ro_table, void *buffer,
|
|||
return proc_dostring(&table, 0, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static int write_actions_logged(struct ctl_table *ro_table, void *buffer,
|
||||
static int write_actions_logged(struct ctl_table *ro_table, void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos, u32 *actions_logged)
|
||||
{
|
||||
char names[sizeof(seccomp_actions_avail)];
|
||||
|
|
@ -1846,7 +2161,7 @@ static int __init seccomp_sysctl_init(void)
|
|||
|
||||
hdr = register_sysctl_paths(seccomp_sysctl_path, seccomp_sysctl_table);
|
||||
if (!hdr)
|
||||
pr_warn("seccomp: sysctl registration failed\n");
|
||||
pr_warn("sysctl registration failed\n");
|
||||
else
|
||||
kmemleak_not_leak(hdr);
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o
|
|||
obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o
|
||||
obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o
|
||||
obj-$(CONFIG_KPROBE_EVENTS) += trace_kprobe.o
|
||||
obj-$(CONFIG_TRACEPOINTS) += error_report-traces.o
|
||||
obj-$(CONFIG_TRACEPOINTS) += power-traces.o
|
||||
ifeq ($(CONFIG_PM),y)
|
||||
obj-$(CONFIG_TRACEPOINTS) += rpm-traces.o
|
||||
|
|
|
|||
12
kernel/trace/error_report-traces.c
Normal file
12
kernel/trace/error_report-traces.c
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Error reporting trace points.
|
||||
*
|
||||
* Copyright (C) 2021, Google LLC.
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/error_report.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(error_report_end);
|
||||
|
||||
Loading…
Reference in a new issue