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
@@ -0,0 +1,2 @@
stub_exe
stub_exe.dbg
+50
View File
@@ -0,0 +1,50 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
#
obj-y := stub.o mmu.o process.o syscall.o uaccess.o \
stub_exe_embed.o
# Stub executable
stub_exe_objs-y := stub_exe.o
stub_exe_objs := $(foreach F,$(stub_exe_objs-y),$(obj)/$F)
# Object file containing the ELF executable
$(obj)/stub_exe_embed.o: $(src)/stub_exe_embed.S $(obj)/stub_exe
$(obj)/stub_exe.dbg: $(stub_exe_objs) FORCE
$(call if_changed,stub_exe)
$(obj)/stub_exe: OBJCOPYFLAGS := -S
$(obj)/stub_exe: $(obj)/stub_exe.dbg FORCE
$(call if_changed,objcopy)
quiet_cmd_stub_exe = STUB_EXE $@
cmd_stub_exe = $(CC) -nostdlib -o $@ \
$(filter-out $(UM_GPROF_OPT) $(UM_GCOV_OPT),$(KBUILD_CFLAGS)) $(STUB_EXE_LDFLAGS) \
$(filter %.o,$^)
STUB_EXE_LDFLAGS = -Wl,-n -static
targets += stub_exe.dbg stub_exe $(stub_exe_objs-y)
# end
# stub.o is in the stub, so it can't be built with profiling
# GCC hardened also auto-enables -fpic, but we need %ebx so it can't work ->
# disable it
CFLAGS_stub.o := $(CFLAGS_NO_HARDENING)
CFLAGS_stub_exe.o := $(CFLAGS_NO_HARDENING)
# Clang will call memset() from __builtin_alloca() when stack variable
# initialization is enabled, which is used in stub_exe.c.
CFLAGS_stub_exe.o += $(call cc-option, -ftrivial-auto-var-init=uninitialized)
UNPROFILE_OBJS := stub.o stub_exe.o
KCOV_INSTRUMENT := n
include $(srctree)/arch/um/scripts/Makefile.rules
+165
View File
@@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/mm.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <shared/irq_kern.h>
#include <asm/pgalloc.h>
#include <asm/sections.h>
#include <asm/mmu_context.h>
#include <as-layout.h>
#include <os.h>
#include <skas.h>
#include <stub-data.h>
/* Ensure the stub_data struct covers the allocated area */
static_assert(sizeof(struct stub_data) == STUB_DATA_PAGES * UM_KERN_PAGE_SIZE);
static spinlock_t mm_list_lock;
static struct list_head mm_list;
struct mutex *__get_turnstile(struct mm_id *mm_id)
{
struct mm_context *ctx = container_of(mm_id, struct mm_context, id);
return &ctx->turnstile;
}
void enter_turnstile(struct mm_id *mm_id)
{
mutex_lock(__get_turnstile(mm_id));
}
void exit_turnstile(struct mm_id *mm_id)
{
mutex_unlock(__get_turnstile(mm_id));
}
int init_new_context(struct task_struct *task, struct mm_struct *mm)
{
struct mm_id *new_id = &mm->context.id;
unsigned long stack = 0;
int ret = -ENOMEM;
mutex_init(&mm->context.turnstile);
spin_lock_init(&mm->context.sync_tlb_lock);
stack = __get_free_pages(GFP_KERNEL | __GFP_ZERO, ilog2(STUB_DATA_PAGES));
if (stack == 0)
goto out;
new_id->stack = stack;
new_id->syscall_data_len = 0;
new_id->syscall_fd_num = 0;
scoped_guard(spinlock_irqsave, &mm_list_lock) {
/* Insert into list, used for lookups when the child dies */
list_add(&mm->context.list, &mm_list);
}
ret = start_userspace(new_id);
if (ret < 0)
goto out_free;
/* Ensure the new MM is clean and nothing unwanted is mapped */
unmap(new_id, 0, STUB_START);
return 0;
out_free:
free_pages(new_id->stack, ilog2(STUB_DATA_PAGES));
out:
return ret;
}
void destroy_context(struct mm_struct *mm)
{
struct mm_context *mmu = &mm->context;
/*
* If init_new_context wasn't called, this will be
* zero, resulting in a kill(0), which will result in the
* whole UML suddenly dying. Also, cover negative and
* 1 cases, since they shouldn't happen either.
*
* Negative cases happen if the child died unexpectedly.
*/
if (mmu->id.pid >= 0 && mmu->id.pid < 2) {
printk(KERN_ERR "corrupt mm_context - pid = %d\n",
mmu->id.pid);
return;
}
scoped_guard(spinlock_irqsave, &mm_list_lock)
list_del(&mm->context.list);
if (mmu->id.pid > 0) {
os_kill_ptraced_process(mmu->id.pid, 1);
mmu->id.pid = -1;
}
if (using_seccomp && mmu->id.sock)
os_close_file(mmu->id.sock);
free_pages(mmu->id.stack, ilog2(STUB_DATA_PAGES));
}
static irqreturn_t mm_sigchld_irq(int irq, void* dev)
{
struct mm_context *mm_context;
pid_t pid;
guard(spinlock)(&mm_list_lock);
while ((pid = os_reap_child()) > 0) {
/*
* A child died, check if we have an MM with the PID. This is
* only relevant in SECCOMP mode (as ptrace will fail anyway).
*
* See wait_stub_done_seccomp for more details.
*/
list_for_each_entry(mm_context, &mm_list, list) {
if (mm_context->id.pid == pid) {
struct stub_data *stub_data;
printk("Unexpectedly lost MM child! Affected tasks will segfault.");
/* Marks the MM as dead */
mm_context->id.pid = -1;
stub_data = (void *)mm_context->id.stack;
stub_data->futex = FUTEX_IN_KERN;
#if IS_ENABLED(CONFIG_SMP)
os_futex_wake(&stub_data->futex);
#endif
/*
* NOTE: Currently executing syscalls by
* affected tasks may finish normally.
*/
break;
}
}
}
return IRQ_HANDLED;
}
static int __init init_child_tracking(void)
{
int err;
spin_lock_init(&mm_list_lock);
INIT_LIST_HEAD(&mm_list);
err = request_irq(SIGCHLD_IRQ, mm_sigchld_irq, 0, "SIGCHLD", NULL);
if (err < 0)
panic("Failed to register SIGCHLD IRQ: %d", err);
return 0;
}
early_initcall(init_child_tracking)
+79
View File
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/init.h>
#include <linux/sched/mm.h>
#include <linux/sched/task_stack.h>
#include <linux/sched/task.h>
#include <linux/smp-internal.h>
#include <asm/tlbflush.h>
#include <as-layout.h>
#include <kern.h>
#include <os.h>
#include <skas.h>
#include <kern_util.h>
extern void start_kernel(void);
static int __init start_kernel_proc(void *unused)
{
block_signals_trace();
start_kernel();
return 0;
}
char cpu_irqstacks[NR_CPUS][THREAD_SIZE] __aligned(THREAD_SIZE);
int __init start_uml(void)
{
stack_protections((unsigned long) &cpu_irqstacks[0]);
set_sigstack(cpu_irqstacks[0], THREAD_SIZE);
init_new_thread_signals();
init_task.thread.request.thread.proc = start_kernel_proc;
init_task.thread.request.thread.arg = NULL;
return start_idle_thread(task_stack_page(&init_task),
&init_task.thread.switch_buf);
}
unsigned long current_stub_stack(void)
{
if (current->mm == NULL)
return 0;
return current->mm->context.id.stack;
}
struct mm_id *current_mm_id(void)
{
if (current->mm == NULL)
return NULL;
return &current->mm->context.id;
}
void current_mm_sync(void)
{
if (current->mm == NULL)
return;
um_tlb_sync(current->mm);
}
static DEFINE_SPINLOCK(initial_jmpbuf_spinlock);
void initial_jmpbuf_lock(void)
{
spin_lock_irq(&initial_jmpbuf_spinlock);
}
void initial_jmpbuf_unlock(void)
{
spin_unlock_irq(&initial_jmpbuf_spinlock);
}
+181
View File
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Benjamin Berg <benjamin@sipsolutions.net>
*/
#include <sysdep/stub.h>
#include <linux/futex.h>
#include <sys/socket.h>
#include <errno.h>
/*
* Known security issues
*
* Userspace can jump to this address to execute *any* syscall that is
* permitted by the stub. As we will return afterwards, it can do
* whatever it likes, including:
* - Tricking the kernel into handing out the memory FD
* - Using this memory FD to read/write all physical memory
* - Running in parallel to the kernel processing a syscall
* (possibly creating data races?)
* - Blocking e.g. SIGALRM to avoid time based scheduling
*
* To avoid this, the permitted location for each syscall needs to be
* checked for in the SECCOMP filter (which is reasonably simple). Also,
* more care will need to go into considerations how the code might be
* tricked by using a prepared stack (or even modifying the stack from
* another thread in case SMP support is added).
*
* As for the SIGALRM, the best counter measure will be to check in the
* kernel that the process is reporting back the SIGALRM in a timely
* fashion.
*/
static __always_inline int syscall_handler(int fd_map[STUB_MAX_FDS])
{
struct stub_data *d = get_stub_data();
int i;
unsigned long res;
int fd;
for (i = 0; i < d->syscall_data_len; i++) {
struct stub_syscall *sc = &d->syscall_data[i];
switch (sc->syscall) {
case STUB_SYSCALL_MMAP:
if (fd_map)
fd = fd_map[sc->mem.fd];
else
fd = sc->mem.fd;
res = stub_syscall6(STUB_MMAP_NR,
sc->mem.addr, sc->mem.length,
sc->mem.prot,
MAP_SHARED | MAP_FIXED,
fd, sc->mem.offset);
if (res != sc->mem.addr) {
d->err = res;
d->syscall_data_len = i;
return -1;
}
break;
case STUB_SYSCALL_MUNMAP:
res = stub_syscall2(__NR_munmap,
sc->mem.addr, sc->mem.length);
if (res) {
d->err = res;
d->syscall_data_len = i;
return -1;
}
break;
default:
d->err = -95; /* EOPNOTSUPP */
d->syscall_data_len = i;
return -1;
}
}
d->err = 0;
d->syscall_data_len = 0;
return 0;
}
void __section(".__syscall_stub")
stub_syscall_handler(void)
{
syscall_handler(NULL);
trap_myself();
}
void __section(".__syscall_stub")
stub_signal_interrupt(int sig, siginfo_t *info, void *p)
{
struct stub_data *d = get_stub_data();
char rcv_data;
union {
char data[CMSG_SPACE(sizeof(int) * STUB_MAX_FDS)];
struct cmsghdr align;
} ctrl = {};
struct iovec iov = {
.iov_base = &rcv_data,
.iov_len = 1,
};
struct msghdr msghdr = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = &ctrl,
.msg_controllen = sizeof(ctrl),
};
ucontext_t *uc = p;
struct cmsghdr *fd_msg;
int *fd_map;
int num_fds;
long res;
d->signal = sig;
d->si_offset = (unsigned long)info - (unsigned long)&d->sigstack[0];
d->mctx_offset = (unsigned long)&uc->uc_mcontext - (unsigned long)&d->sigstack[0];
restart_wait:
d->futex = FUTEX_IN_KERN;
do {
res = stub_syscall3(__NR_futex, (unsigned long)&d->futex,
FUTEX_WAKE, 1);
} while (res == -EINTR);
do {
res = stub_syscall4(__NR_futex, (unsigned long)&d->futex,
FUTEX_WAIT, FUTEX_IN_KERN, 0);
} while (res == -EINTR || d->futex == FUTEX_IN_KERN);
if (res < 0 && res != -EAGAIN)
stub_syscall1(__NR_exit_group, 1);
if (d->syscall_data_len) {
/* Read passed FDs (if any) */
do {
res = stub_syscall3(__NR_recvmsg, 0, (unsigned long)&msghdr, 0);
} while (res == -EINTR);
/* We should never have a receive error (other than -EAGAIN) */
if (res < 0 && res != -EAGAIN)
stub_syscall1(__NR_exit_group, 1);
/* Receive the FDs */
num_fds = 0;
fd_msg = msghdr.msg_control;
fd_map = (void *)CMSG_DATA(fd_msg);
if (res == iov.iov_len && msghdr.msg_controllen > sizeof(struct cmsghdr))
num_fds = (fd_msg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
/* Try running queued syscalls. */
res = syscall_handler(fd_map);
while (num_fds)
stub_syscall2(__NR_close, fd_map[--num_fds], 0);
} else {
res = 0;
}
if (res < 0 || d->restart_wait) {
/* Report SIGSYS if we restart. */
d->signal = SIGSYS;
d->restart_wait = 0;
goto restart_wait;
}
/* Restore arch dependent state that is not part of the mcontext */
stub_seccomp_restore_state(&d->arch_data);
/* Return so that the host modified mcontext is restored. */
}
void __section(".__syscall_stub")
stub_signal_restorer(void)
{
/* We must not have anything on the stack when doing rt_sigreturn */
stub_syscall0(__NR_rt_sigreturn);
}
+230
View File
@@ -0,0 +1,230 @@
#include <sys/ptrace.h>
#include <sys/prctl.h>
#include <sys/fcntl.h>
#include <asm/unistd.h>
#include <sysdep/stub.h>
#include <stub-data.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <generated/asm-offsets.h>
void _start(void);
noinline static void real_init(void)
{
struct stub_init_data init_data;
unsigned long res;
struct {
void *ss_sp;
int ss_flags;
size_t ss_size;
} stack = {
.ss_size = STUB_DATA_PAGES * UM_KERN_PAGE_SIZE,
};
struct {
void *sa_handler_;
unsigned long sa_flags;
void *sa_restorer;
unsigned long long sa_mask;
} sa = {
/* Need to set SA_RESTORER (but the handler never returns) */
.sa_flags = SA_ONSTACK | SA_NODEFER | SA_SIGINFO | 0x04000000,
};
/* set a nice name */
stub_syscall2(__NR_prctl, PR_SET_NAME, (unsigned long)"uml-userspace");
/* Make sure this process dies if the kernel dies */
stub_syscall2(__NR_prctl, PR_SET_PDEATHSIG, SIGKILL);
/* Needed in SECCOMP mode (and safe to do anyway) */
stub_syscall5(__NR_prctl, PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
/* read information from STDIN and close it */
res = stub_syscall3(__NR_read, 0,
(unsigned long)&init_data, sizeof(init_data));
if (res != sizeof(init_data))
stub_syscall1(__NR_exit, 10);
/* In SECCOMP mode, FD 0 is a socket and is later used for FD passing */
if (!init_data.seccomp)
stub_syscall1(__NR_close, 0);
else
stub_syscall3(__NR_fcntl, 0, F_SETFL, O_NONBLOCK);
/* map stub code + data */
res = stub_syscall6(STUB_MMAP_NR,
init_data.stub_start, UM_KERN_PAGE_SIZE,
PROT_READ | PROT_EXEC, MAP_FIXED | MAP_SHARED,
init_data.stub_code_fd, init_data.stub_code_offset);
if (res != init_data.stub_start)
stub_syscall1(__NR_exit, 11);
res = stub_syscall6(STUB_MMAP_NR,
init_data.stub_start + UM_KERN_PAGE_SIZE,
STUB_DATA_PAGES * UM_KERN_PAGE_SIZE,
PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED,
init_data.stub_data_fd, init_data.stub_data_offset);
if (res != init_data.stub_start + UM_KERN_PAGE_SIZE)
stub_syscall1(__NR_exit, 12);
/* In SECCOMP mode, we only need the signalling FD from now on */
if (init_data.seccomp) {
res = stub_syscall3(__NR_close_range, 1, ~0U, 0);
if (res != 0)
stub_syscall1(__NR_exit, 13);
}
/* setup signal stack inside stub data */
stack.ss_sp = (void *)init_data.stub_start + UM_KERN_PAGE_SIZE;
stub_syscall2(__NR_sigaltstack, (unsigned long)&stack, 0);
/* register signal handlers */
sa.sa_handler_ = (void *) init_data.signal_handler;
sa.sa_restorer = (void *) init_data.signal_restorer;
if (!init_data.seccomp) {
/* In ptrace mode, the SIGSEGV handler never returns */
sa.sa_mask = 0;
res = stub_syscall4(__NR_rt_sigaction, SIGSEGV,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 14);
} else {
/* SECCOMP mode uses rt_sigreturn, need to mask all signals */
sa.sa_mask = ~0ULL;
res = stub_syscall4(__NR_rt_sigaction, SIGSEGV,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 15);
res = stub_syscall4(__NR_rt_sigaction, SIGSYS,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 16);
res = stub_syscall4(__NR_rt_sigaction, SIGALRM,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 17);
res = stub_syscall4(__NR_rt_sigaction, SIGTRAP,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 18);
res = stub_syscall4(__NR_rt_sigaction, SIGILL,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 19);
res = stub_syscall4(__NR_rt_sigaction, SIGFPE,
(unsigned long)&sa, 0, sizeof(sa.sa_mask));
if (res != 0)
stub_syscall1(__NR_exit, 20);
}
/*
* If in seccomp mode, install the SECCOMP filter and trigger a syscall.
* Otherwise set PTRACE_TRACEME and do a SIGSTOP.
*/
if (init_data.seccomp) {
struct sock_filter filter[] = {
#if __BITS_PER_LONG > 32
/* [0] Load upper 32bit of instruction pointer from seccomp_data */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
(offsetof(struct seccomp_data, instruction_pointer) + 4)),
/* [1] Jump forward 3 instructions if the upper address is not identical */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (init_data.stub_start) >> 32, 0, 3),
#endif
/* [2] Load lower 32bit of instruction pointer from seccomp_data */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
(offsetof(struct seccomp_data, instruction_pointer))),
/* [3] Mask out lower bits */
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xfffff000),
/* [4] Jump to [6] if the lower bits are not on the expected page */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (init_data.stub_start) & 0xfffff000, 1, 0),
/* [5] Trap call, allow */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
/* [6,7] Check architecture */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
offsetof(struct seccomp_data, arch)),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,
UM_SECCOMP_ARCH_NATIVE, 1, 0),
/* [8] Kill (for architecture check) */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* [9] Load syscall number */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
offsetof(struct seccomp_data, nr)),
/* [10-16] Check against permitted syscalls */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_futex,
7, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_recvmsg,
6, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,__NR_close,
5, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, STUB_MMAP_NR,
4, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_munmap,
3, 0),
#ifdef __i386__
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_set_thread_area,
2, 0),
#else
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_arch_prctl,
2, 0),
#endif
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_rt_sigreturn,
1, 0),
/* [17] Not one of the permitted syscalls */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
/* [18] Permitted call for the stub */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter,
};
if (stub_syscall3(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
SECCOMP_FILTER_FLAG_TSYNC,
(unsigned long)&prog) != 0)
stub_syscall1(__NR_exit, 21);
/* Fall through, the exit syscall will cause SIGSYS */
} else {
stub_syscall4(__NR_ptrace, PTRACE_TRACEME, 0, 0, 0);
stub_syscall2(__NR_kill, stub_syscall0(__NR_getpid), SIGSTOP);
}
stub_syscall1(__NR_exit, 30);
__builtin_unreachable();
}
__attribute__((naked)) void _start(void)
{
/*
* Since the stack after exec() starts at the top-most address,
* but that's exactly where we also want to map the stub data
* and code, this must:
* - push the stack by 1 code and STUB_DATA_PAGES data pages
* - call real_init()
* This way, real_init() can use the stack normally, while the
* original stack further down (higher address) will become
* inaccessible after the mmap() calls above.
*/
stub_start(real_init);
}
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/init.h>
#include <linux/linkage.h>
__INITDATA
SYM_DATA_START(stub_exe_start)
.incbin "arch/um/kernel/skas/stub_exe"
SYM_DATA_END_LABEL(stub_exe_start, SYM_L_GLOBAL, stub_exe_end)
__FINIT
+74
View File
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/seccomp.h>
#include <kern_util.h>
#include <sysdep/ptrace.h>
#include <sysdep/ptrace_user.h>
#include <linux/time-internal.h>
#include <asm/syscall.h>
#include <asm/unistd.h>
#include <asm/delay.h>
void handle_syscall(struct uml_pt_regs *r)
{
struct pt_regs *regs = container_of(r, struct pt_regs, regs);
int syscall;
/* Initialize the syscall number and default return value. */
UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp);
PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS);
if (syscall_trace_enter(regs))
goto out;
/* Do the seccomp check after ptrace; failures should be fast. */
if (secure_computing() == -1)
goto out;
syscall = UPT_SYSCALL_NR(r);
/*
* If no time passes, then sched_yield may not actually yield, causing
* broken spinlock implementations in userspace (ASAN) to hang for long
* periods of time.
*/
if ((time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL) &&
syscall == __NR_sched_yield)
tt_extra_sched_jiffies += 1;
if (syscall >= 0 && syscall < __NR_syscalls) {
unsigned long ret;
ret = (*sys_call_table[syscall])(UPT_SYSCALL_ARG1(&regs->regs),
UPT_SYSCALL_ARG2(&regs->regs),
UPT_SYSCALL_ARG3(&regs->regs),
UPT_SYSCALL_ARG4(&regs->regs),
UPT_SYSCALL_ARG5(&regs->regs),
UPT_SYSCALL_ARG6(&regs->regs));
PT_REGS_SET_SYSCALL_RETURN(regs, ret);
/*
* An error value here can be some form of -ERESTARTSYS
* and then we'd just loop. Make any error syscalls take
* some time, so that it won't just loop if something is
* not ready, and hopefully other things will make some
* progress.
*/
if (IS_ERR_VALUE(ret) &&
(time_travel_mode == TT_MODE_INFCPU ||
time_travel_mode == TT_MODE_EXTERNAL)) {
um_udelay(1);
schedule();
}
}
out:
syscall_trace_leave(regs);
}
+368
View File
@@ -0,0 +1,368 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
*/
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/page.h>
#include <kern_util.h>
#include <asm/futex.h>
#include <os.h>
pte_t *virt_to_pte(struct mm_struct *mm, unsigned long addr)
{
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
if (mm == NULL)
return NULL;
pgd = pgd_offset(mm, addr);
if (!pgd_present(*pgd))
return NULL;
p4d = p4d_offset(pgd, addr);
if (!p4d_present(*p4d))
return NULL;
pud = pud_offset(p4d, addr);
if (!pud_present(*pud))
return NULL;
pmd = pmd_offset(pud, addr);
if (!pmd_present(*pmd))
return NULL;
return pte_offset_kernel(pmd, addr);
}
static pte_t *maybe_map(unsigned long virt, int is_write)
{
pte_t *pte = virt_to_pte(current->mm, virt);
int err, dummy_code;
if ((pte == NULL) || !pte_present(*pte) ||
(is_write && !pte_write(*pte))) {
err = handle_page_fault(virt, 0, is_write, 1, &dummy_code);
if (err)
return NULL;
pte = virt_to_pte(current->mm, virt);
}
if (!pte_present(*pte))
pte = NULL;
return pte;
}
static int do_op_one_page(unsigned long addr, int len, int is_write,
int (*op)(unsigned long addr, int len, void *arg), void *arg)
{
struct page *page;
pte_t *pte;
int n;
pte = maybe_map(addr, is_write);
if (pte == NULL)
return -1;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
addr = (unsigned long) page_address(page) +
(addr & ~PAGE_MASK);
#else
addr = (unsigned long) kmap_atomic(page) +
(addr & ~PAGE_MASK);
#endif
n = (*op)(addr, len, arg);
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic((void *)addr);
#endif
return n;
}
static long buffer_op(unsigned long addr, int len, int is_write,
int (*op)(unsigned long, int, void *), void *arg)
{
long size, remain, n;
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
remain = len;
n = do_op_one_page(addr, size, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
addr += size;
remain -= size;
if (remain == 0)
goto out;
while (addr < ((addr + remain) & PAGE_MASK)) {
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
addr += PAGE_SIZE;
remain -= PAGE_SIZE;
}
if (remain == 0)
goto out;
n = do_op_one_page(addr, remain, is_write, op, arg);
if (n != 0) {
remain = (n < 0 ? remain : 0);
goto out;
}
return 0;
out:
return remain;
}
static int copy_chunk_from_user(unsigned long from, int len, void *arg)
{
unsigned long *to_ptr = arg, to = *to_ptr;
memcpy((void *) to, (void *) from, len);
*to_ptr += len;
return 0;
}
unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to);
}
EXPORT_SYMBOL(raw_copy_from_user);
static int copy_chunk_to_user(unsigned long to, int len, void *arg)
{
unsigned long *from_ptr = arg, from = *from_ptr;
memcpy((void *) to, (void *) from, len);
*from_ptr += len;
return 0;
}
unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
return buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from);
}
EXPORT_SYMBOL(raw_copy_to_user);
static int strncpy_chunk_from_user(unsigned long from, int len, void *arg)
{
char **to_ptr = arg, *to = *to_ptr;
int n;
n = strnlen((void *) from, len);
memcpy_and_pad(to, len, (void *) from, n, 0);
*to_ptr += n;
if (n < len)
return 1;
return 0;
}
long strncpy_from_user(char *dst, const char __user *src, long count)
{
long n;
char *ptr = dst;
if (!access_ok(src, 1))
return -EFAULT;
n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user,
&ptr);
if (n != 0)
return -EFAULT;
return strnlen(dst, count);
}
EXPORT_SYMBOL(strncpy_from_user);
static int clear_chunk(unsigned long addr, int len, void *unused)
{
memset((void *) addr, 0, len);
return 0;
}
unsigned long __clear_user(void __user *mem, unsigned long len)
{
return buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL);
}
EXPORT_SYMBOL(__clear_user);
static int strnlen_chunk(unsigned long str, int len, void *arg)
{
int *len_ptr = arg, n;
n = strnlen((void *) str, len);
*len_ptr += n;
if (n < len)
return 1;
return 0;
}
long strnlen_user(const char __user *str, long len)
{
int count = 0, n;
if (!access_ok(str, 1))
return -EFAULT;
n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count);
if (n == 0)
return count + 1;
return 0;
}
EXPORT_SYMBOL(strnlen_user);
/**
* arch_futex_atomic_op_inuser() - Atomic arithmetic operation with constant
* argument and comparison of the previous
* futex value with another constant.
*
* @op: operation to execute
* @oparg: argument to operation
* @oval: old value at uaddr
* @uaddr: pointer to user space address
*
* Return:
* 0 - On success
* -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
* -ENOSYS - Operation not supported
*/
int arch_futex_atomic_op_inuser(int op, u32 oparg, int *oval, u32 __user *uaddr)
{
int oldval, ret;
struct page *page;
unsigned long addr = (unsigned long) uaddr;
pte_t *pte;
ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
preempt_disable();
pte = maybe_map(addr, 1);
if (pte == NULL)
goto out_inuser;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
addr = (unsigned long) page_address(page) +
(((unsigned long) addr) & ~PAGE_MASK);
#else
addr = (unsigned long) kmap_atomic(page) +
((unsigned long) addr & ~PAGE_MASK);
#endif
uaddr = (u32 *) addr;
oldval = *uaddr;
ret = 0;
switch (op) {
case FUTEX_OP_SET:
*uaddr = oparg;
break;
case FUTEX_OP_ADD:
*uaddr += oparg;
break;
case FUTEX_OP_OR:
*uaddr |= oparg;
break;
case FUTEX_OP_ANDN:
*uaddr &= ~oparg;
break;
case FUTEX_OP_XOR:
*uaddr ^= oparg;
break;
default:
ret = -ENOSYS;
}
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic((void *)addr);
#endif
out_inuser:
preempt_enable();
if (ret == 0)
*oval = oldval;
return ret;
}
EXPORT_SYMBOL(arch_futex_atomic_op_inuser);
/**
* futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
* uaddr with newval if the current value is
* oldval.
* @uval: pointer to store content of @uaddr
* @uaddr: pointer to user space address
* @oldval: old value
* @newval: new value to store to @uaddr
*
* Return:
* 0 - On success
* -EFAULT - User access resulted in a page fault
* -EAGAIN - Atomic operation was unable to complete due to contention
*/
int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
struct page *page;
pte_t *pte;
int ret = -EFAULT;
if (!access_ok(uaddr, sizeof(*uaddr)))
return -EFAULT;
preempt_disable();
pte = maybe_map((unsigned long) uaddr, 1);
if (pte == NULL)
goto out_inatomic;
page = pte_page(*pte);
#ifdef CONFIG_64BIT
pagefault_disable();
uaddr = page_address(page) + (((unsigned long) uaddr) & ~PAGE_MASK);
#else
uaddr = kmap_atomic(page) + ((unsigned long) uaddr & ~PAGE_MASK);
#endif
*uval = *uaddr;
ret = cmpxchg(uaddr, oldval, newval);
#ifdef CONFIG_64BIT
pagefault_enable();
#else
kunmap_atomic(uaddr);
#endif
ret = 0;
out_inatomic:
preempt_enable();
return ret;
}
EXPORT_SYMBOL(futex_atomic_cmpxchg_inatomic);