Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0
# Prevent the noinstr section from being pestered by sanitizer and other goodies
# as long as these things cannot be disabled per function.
KASAN_SANITIZE := n
UBSAN_SANITIZE := n
KCOV_INSTRUMENT := n
# Branch profiling isn't noinstr-safe
ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING
CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong
CFLAGS_common.o += -fno-stack-protector
obj-$(CONFIG_GENERIC_IRQ_ENTRY) += common.o
obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o syscall_user_dispatch.o
obj-$(CONFIG_VIRT_XFER_TO_GUEST_WORK) += virt.o
+205
View File
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/futex.h>
#include <linux/highmem.h>
#include <linux/irq-entry-common.h>
#include <linux/jump_label.h>
#include <linux/kmsan.h>
#include <linux/livepatch.h>
#include <linux/resume_user_mode.h>
#include <linux/tick.h>
/* Workaround to allow gradual conversion of architecture code */
void __weak arch_do_signal_or_restart(struct pt_regs *regs) { }
#ifdef CONFIG_HAVE_GENERIC_TIF_BITS
#define EXIT_TO_USER_MODE_WORK_LOOP (EXIT_TO_USER_MODE_WORK & ~_TIF_RSEQ)
#else
#define EXIT_TO_USER_MODE_WORK_LOOP (EXIT_TO_USER_MODE_WORK)
#endif
/* TIF bits, which prevent a time slice extension. */
#ifdef CONFIG_PREEMPT_RT
/*
* Since rseq slice ext has a direct correlation to the worst case
* scheduling latency (schedule is delayed after all), only have it affect
* LAZY reschedules on PREEMPT_RT for now.
*
* However, since this delay is only applicable to userspace, a value
* for rseq_slice_extension_nsec that is strictly less than the worst case
* kernel space preempt_disable() region, should mean the scheduling latency
* is not affected, even for !LAZY.
*
* However, since this value depends on the hardware at hand, it cannot be
* pre-determined in any sensible way. Hence punt on this problem for now.
*/
# define TIF_SLICE_EXT_SCHED (_TIF_NEED_RESCHED_LAZY)
#else
# define TIF_SLICE_EXT_SCHED (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)
#endif
#define TIF_SLICE_EXT_DENY (EXIT_TO_USER_MODE_WORK & ~TIF_SLICE_EXT_SCHED)
static __always_inline unsigned long __exit_to_user_mode_loop(struct pt_regs *regs,
unsigned long ti_work)
{
/*
* Before returning to user space ensure that all pending work
* items have been completed.
*/
while (ti_work & EXIT_TO_USER_MODE_WORK_LOOP) {
local_irq_enable();
if (ti_work & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)) {
if (!rseq_grant_slice_extension(ti_work, TIF_SLICE_EXT_DENY))
schedule();
}
if (ti_work & _TIF_UPROBE)
uprobe_notify_resume(regs);
if (ti_work & _TIF_PATCH_PENDING)
klp_update_patch_state(current);
if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) {
futex_fixup_robust_unlock(regs);
arch_do_signal_or_restart(regs);
}
if (ti_work & _TIF_NOTIFY_RESUME)
resume_user_mode_work(regs);
/* Architecture specific TIF work */
arch_exit_to_user_mode_work(regs, ti_work);
/*
* Disable interrupts and reevaluate the work flags as they
* might have changed while interrupts and preemption was
* enabled above.
*/
local_irq_disable();
/* Check if any of the above work has queued a deferred wakeup */
tick_nohz_user_enter_prepare();
ti_work = read_thread_flags();
}
/* Return the latest work state for arch_exit_to_user_mode() */
return ti_work;
}
/**
* exit_to_user_mode_loop - do any pending work before leaving to user space
* @regs: Pointer to pt_regs on entry stack
* @ti_work: TIF work flags as read by the caller
*/
__always_inline unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
unsigned long ti_work)
{
for (;;) {
ti_work = __exit_to_user_mode_loop(regs, ti_work);
if (likely(!rseq_exit_to_user_mode_restart(regs, ti_work)))
return ti_work;
ti_work = read_thread_flags();
}
}
noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
{
if (user_mode(regs)) {
irqentry_state_t ret = {
.exit_rcu = false,
};
irqentry_enter_from_user_mode(regs);
return ret;
}
return irqentry_enter_from_kernel_mode(regs);
}
/**
* arch_irqentry_exit_need_resched - Architecture specific need resched function
*
* Invoked from raw_irqentry_exit_cond_resched() to check if resched is needed.
* Defaults return true.
*
* The main purpose is to permit arch to avoid preemption of a task from an IRQ.
*/
static inline bool arch_irqentry_exit_need_resched(void);
#ifndef arch_irqentry_exit_need_resched
static inline bool arch_irqentry_exit_need_resched(void) { return true; }
#endif
void raw_irqentry_exit_cond_resched(void)
{
if (!preempt_count()) {
/* Sanity check RCU and thread stack */
rcu_irq_exit_check_preempt();
if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
WARN_ON_ONCE(!on_thread_stack());
if (need_resched() && arch_irqentry_exit_need_resched())
preempt_schedule_irq();
}
}
#ifdef CONFIG_PREEMPT_DYNAMIC
#if defined(CONFIG_HAVE_PREEMPT_DYNAMIC_CALL)
DEFINE_STATIC_CALL(irqentry_exit_cond_resched, raw_irqentry_exit_cond_resched);
#elif defined(CONFIG_HAVE_PREEMPT_DYNAMIC_KEY)
DEFINE_STATIC_KEY_TRUE(sk_dynamic_irqentry_exit_cond_resched);
void dynamic_irqentry_exit_cond_resched(void)
{
if (!static_branch_unlikely(&sk_dynamic_irqentry_exit_cond_resched))
return;
raw_irqentry_exit_cond_resched();
}
#endif
#endif
noinstr void irqentry_exit(struct pt_regs *regs, irqentry_state_t state)
{
if (user_mode(regs))
irqentry_exit_to_user_mode(regs);
else
irqentry_exit_to_kernel_mode(regs, state);
}
irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs)
{
irqentry_state_t irq_state;
irq_state.lockdep = lockdep_hardirqs_enabled();
__nmi_enter();
lockdep_hardirqs_off(CALLER_ADDR0);
lockdep_hardirq_enter();
ct_nmi_enter();
instrumentation_begin();
kmsan_unpoison_entry_regs(regs);
trace_hardirqs_off_finish();
ftrace_nmi_enter();
instrumentation_end();
return irq_state;
}
void noinstr irqentry_nmi_exit(struct pt_regs *regs, irqentry_state_t irq_state)
{
instrumentation_begin();
ftrace_nmi_exit();
if (irq_state.lockdep) {
trace_hardirqs_on_prepare();
lockdep_hardirqs_on_prepare();
}
instrumentation_end();
ct_nmi_exit();
lockdep_hardirq_exit();
if (irq_state.lockdep)
lockdep_hardirqs_on(CALLER_ADDR0);
__nmi_exit();
}
+23
View File
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/entry-common.h>
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
/* Out of line to prevent tracepoint code duplication */
long trace_syscall_enter(struct pt_regs *regs, long syscall)
{
trace_sys_enter(regs, syscall);
/*
* Probes or BPF hooks in the tracepoint may have changed the
* system call number. Reread it.
*/
return syscall_get_nr(current, regs);
}
void trace_syscall_exit(struct pt_regs *regs, long ret)
{
trace_sys_exit(regs, ret);
}
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020 Collabora Ltd.
*/
#include <linux/entry-common.h>
#include <linux/sched.h>
#include <linux/prctl.h>
#include <linux/ptrace.h>
#include <linux/syscall_user_dispatch.h>
#include <linux/uaccess.h>
#include <linux/signal.h>
#include <linux/elf.h>
#include <linux/sched/signal.h>
#include <linux/sched/task_stack.h>
#include <asm/syscall.h>
static void trigger_sigsys(struct pt_regs *regs)
{
struct kernel_siginfo info;
clear_siginfo(&info);
info.si_signo = SIGSYS;
info.si_code = SYS_USER_DISPATCH;
info.si_call_addr = (void __user *)KSTK_EIP(current);
info.si_errno = 0;
info.si_arch = syscall_get_arch(current);
info.si_syscall = syscall_get_nr(current, regs);
force_sig_info(&info);
}
bool syscall_user_dispatch(struct pt_regs *regs)
{
struct syscall_user_dispatch *sd = &current->syscall_dispatch;
char state;
if (likely(instruction_pointer(regs) - sd->offset < sd->len))
return false;
if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
return false;
if (likely(sd->selector)) {
/*
* access_ok() is performed once, at prctl time, when
* the selector is loaded by userspace.
*/
if (unlikely(__get_user(state, sd->selector))) {
force_exit_sig(SIGSEGV);
return true;
}
if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
return false;
if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
force_exit_sig(SIGSYS);
return true;
}
}
sd->on_dispatch = true;
syscall_rollback(current, regs);
trigger_sigsys(regs);
return true;
}
static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode,
unsigned long offset, unsigned long len,
char __user *selector)
{
switch (mode) {
case PR_SYS_DISPATCH_OFF:
if (offset || len || selector)
return -EINVAL;
break;
case PR_SYS_DISPATCH_EXCLUSIVE_ON:
/*
* Validate the direct dispatcher region just for basic
* sanity against overflow and a 0-sized dispatcher
* region. If the user is able to submit a syscall from
* an address, that address is obviously valid.
*/
if (offset && offset + len <= offset)
return -EINVAL;
break;
case PR_SYS_DISPATCH_INCLUSIVE_ON:
if (len == 0 || offset + len <= offset)
return -EINVAL;
/*
* Invert the range, the check in syscall_user_dispatch()
* supports wrap-around.
*/
offset = offset + len;
len = -len;
break;
default:
return -EINVAL;
}
/*
* access_ok() will clear memory tags for tagged addresses
* if current has memory tagging enabled.
*
* To enable a tracer to set a tracees selector the
* selector address must be untagged for access_ok(),
* otherwise an untagged tracer will always fail to set a
* tagged tracees selector.
*/
if (mode != PR_SYS_DISPATCH_OFF && selector &&
!access_ok(untagged_addr(selector), sizeof(*selector)))
return -EFAULT;
task->syscall_dispatch.selector = selector;
task->syscall_dispatch.offset = offset;
task->syscall_dispatch.len = len;
task->syscall_dispatch.on_dispatch = false;
if (mode != PR_SYS_DISPATCH_OFF)
set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
else
clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);
return 0;
}
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
unsigned long len, char __user *selector)
{
return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
}
int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
void __user *data)
{
struct syscall_user_dispatch *sd = &task->syscall_dispatch;
struct ptrace_sud_config cfg;
if (size != sizeof(cfg))
return -EINVAL;
if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
cfg.mode = PR_SYS_DISPATCH_ON;
else
cfg.mode = PR_SYS_DISPATCH_OFF;
cfg.offset = sd->offset;
cfg.len = sd->len;
cfg.selector = (__u64)(uintptr_t)sd->selector;
if (copy_to_user(data, &cfg, sizeof(cfg)))
return -EFAULT;
return 0;
}
int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
void __user *data)
{
struct ptrace_sud_config cfg;
if (size != sizeof(cfg))
return -EINVAL;
if (copy_from_user(&cfg, data, sizeof(cfg)))
return -EFAULT;
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
(char __user *)(uintptr_t)cfg.selector);
}
+46
View File
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/entry-virt.h>
static int xfer_to_guest_mode_work(unsigned long ti_work)
{
do {
int ret;
if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
return -EINTR;
if (ti_work & (_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY))
schedule();
if (ti_work & _TIF_NOTIFY_RESUME)
resume_user_mode_work(NULL);
ret = arch_xfer_to_guest_mode_handle_work(ti_work);
if (ret)
return ret;
ti_work = read_thread_flags();
} while (ti_work & XFER_TO_GUEST_MODE_WORK);
return 0;
}
int xfer_to_guest_mode_handle_work(void)
{
unsigned long ti_work;
/*
* This is invoked from the outer guest loop with interrupts and
* preemption enabled.
*
* KVM invokes xfer_to_guest_mode_work_pending() with interrupts
* disabled in the inner loop before going into guest mode. No need
* to disable interrupts here.
*/
ti_work = read_thread_flags();
if (!(ti_work & XFER_TO_GUEST_MODE_WORK))
return 0;
return xfer_to_guest_mode_work(ti_work);
}
EXPORT_SYMBOL_GPL(xfer_to_guest_mode_handle_work);