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:
Michael Bestas 2026-05-07 18:19:43 +03:00
commit 376925d57f
No known key found for this signature in database
209 changed files with 10117 additions and 1291 deletions

View file

@ -274,6 +274,7 @@ repeat:
}
write_unlock_irq(&tasklist_lock);
seccomp_filter_release(p);
release_thread(p);
put_task_struct_rcu_user(p);

View file

@ -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);

View file

@ -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(&current->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);

View file

@ -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

View 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);