2019-06-07 20:02:01 +02:00
|
|
|
#include <Kernel/Arch/i386/CPU.h>
|
2019-07-19 21:08:26 +10:00
|
|
|
#include <Kernel/Process.h>
|
2019-04-22 18:44:45 +02:00
|
|
|
#include <Kernel/ProcessTracer.h>
|
2020-01-03 12:48:28 +01:00
|
|
|
#include <Kernel/Random.h>
|
2019-06-07 11:43:58 +02:00
|
|
|
#include <Kernel/Syscall.h>
|
2019-11-17 12:11:43 +01:00
|
|
|
#include <Kernel/VM/MemoryManager.h>
|
2018-10-16 11:01:38 +02:00
|
|
|
|
2019-12-14 16:09:07 +01:00
|
|
|
extern "C" void syscall_handler(RegisterDump);
|
|
|
|
extern "C" void syscall_asm_entry();
|
2018-10-16 11:01:38 +02:00
|
|
|
|
2018-11-09 12:20:44 +01:00
|
|
|
asm(
|
2019-12-14 16:09:07 +01:00
|
|
|
".globl syscall_asm_entry\n"
|
|
|
|
"syscall_asm_entry:\n"
|
2019-10-05 03:31:34 +13:00
|
|
|
" pushl $0x0\n"
|
2018-10-16 11:01:38 +02:00
|
|
|
" pusha\n"
|
2019-12-15 12:11:39 +01:00
|
|
|
" pushl %ds\n"
|
|
|
|
" pushl %es\n"
|
|
|
|
" pushl %fs\n"
|
|
|
|
" pushl %gs\n"
|
|
|
|
" pushl %ss\n"
|
|
|
|
" mov $0x10, %ax\n"
|
|
|
|
" mov %ax, %ds\n"
|
|
|
|
" mov %ax, %es\n"
|
2019-11-09 22:40:35 +01:00
|
|
|
" cld\n"
|
2019-12-14 16:09:07 +01:00
|
|
|
" call syscall_handler\n"
|
2019-12-15 12:11:39 +01:00
|
|
|
" add $0x4, %esp\n"
|
|
|
|
" popl %gs\n"
|
|
|
|
" popl %fs\n"
|
|
|
|
" popl %es\n"
|
|
|
|
" popl %ds\n"
|
2018-10-16 11:01:38 +02:00
|
|
|
" popa\n"
|
2019-10-05 03:31:34 +13:00
|
|
|
" add $0x4, %esp\n"
|
2019-06-07 11:43:58 +02:00
|
|
|
" iret\n");
|
2018-10-16 11:01:38 +02:00
|
|
|
|
|
|
|
namespace Syscall {
|
|
|
|
|
2019-11-09 22:18:16 +01:00
|
|
|
static int handle(RegisterDump&, u32 function, u32 arg1, u32 arg2, u32 arg3);
|
|
|
|
|
2018-10-16 11:01:38 +02:00
|
|
|
void initialize()
|
|
|
|
{
|
2019-12-14 16:09:07 +01:00
|
|
|
register_user_callable_interrupt_handler(0x82, syscall_asm_entry);
|
2019-02-26 12:57:02 +01:00
|
|
|
kprintf("Syscall: int 0x82 handler installed\n");
|
2018-10-16 11:01:38 +02:00
|
|
|
}
|
|
|
|
|
2019-11-09 22:18:16 +01:00
|
|
|
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
|
|
typedef int (Process::*Handler)(u32, u32, u32);
|
2019-11-17 20:01:03 +01:00
|
|
|
#define __ENUMERATE_REMOVED_SYSCALL(x) nullptr,
|
2019-11-09 22:18:16 +01:00
|
|
|
#define __ENUMERATE_SYSCALL(x) reinterpret_cast<Handler>(&Process::sys$##x),
|
|
|
|
static Handler s_syscall_table[] = {
|
|
|
|
ENUMERATE_SYSCALLS
|
|
|
|
};
|
|
|
|
#undef __ENUMERATE_SYSCALL
|
2019-11-17 20:01:03 +01:00
|
|
|
#undef __ENUMERATE_REMOVED_SYSCALL
|
2018-12-20 00:39:29 +01:00
|
|
|
|
2019-11-09 22:18:16 +01:00
|
|
|
int handle(RegisterDump& regs, u32 function, u32 arg1, u32 arg2, u32 arg3)
|
2018-10-16 11:01:38 +02:00
|
|
|
{
|
2018-10-29 21:54:11 +01:00
|
|
|
ASSERT_INTERRUPTS_ENABLED();
|
2019-11-09 22:18:16 +01:00
|
|
|
auto& process = current->process();
|
2019-11-26 21:35:24 +01:00
|
|
|
current->did_syscall();
|
2019-11-09 22:18:16 +01:00
|
|
|
|
|
|
|
if (function == SC_exit || function == SC_exit_thread) {
|
|
|
|
// These syscalls need special handling since they never return to the caller.
|
2018-10-23 15:19:02 +02:00
|
|
|
cli();
|
2019-11-09 22:18:16 +01:00
|
|
|
if (auto* tracer = process.tracer())
|
2019-05-02 15:49:48 +02:00
|
|
|
tracer->did_syscall(function, arg1, arg2, arg3, 0);
|
2019-11-09 22:18:16 +01:00
|
|
|
if (function == SC_exit)
|
|
|
|
process.sys$exit((int)arg1);
|
|
|
|
else
|
2019-11-13 21:49:24 +01:00
|
|
|
process.sys$exit_thread((void*)arg1);
|
2018-10-22 11:43:55 +02:00
|
|
|
ASSERT_NOT_REACHED();
|
|
|
|
return 0;
|
2019-07-19 17:58:12 +10:00
|
|
|
}
|
2019-11-09 22:18:16 +01:00
|
|
|
|
|
|
|
if (function == SC_fork)
|
|
|
|
return process.sys$fork(regs);
|
|
|
|
|
|
|
|
if (function == SC_sigreturn)
|
|
|
|
return process.sys$sigreturn(regs);
|
|
|
|
|
|
|
|
if (function >= Function::__Count) {
|
|
|
|
dbg() << process << ": Unknown syscall %u requested (" << arg1 << ", " << arg2 << ", " << arg3 << ")";
|
2019-05-23 17:02:58 +02:00
|
|
|
return -ENOSYS;
|
2018-10-16 11:01:38 +02:00
|
|
|
}
|
2019-11-09 22:18:16 +01:00
|
|
|
|
2019-11-18 12:35:14 +01:00
|
|
|
if (s_syscall_table[function] == nullptr) {
|
|
|
|
dbg() << process << ": Null syscall " << function << " requested: \"" << to_string((Function)function) << "\", you probably need to rebuild this program.";
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
2019-11-09 22:18:16 +01:00
|
|
|
return (process.*(s_syscall_table[function]))(arg1, arg2, arg3);
|
2018-10-16 11:01:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-12-14 16:09:07 +01:00
|
|
|
void syscall_handler(RegisterDump regs)
|
2018-10-16 11:01:38 +02:00
|
|
|
{
|
2020-01-05 18:00:15 +01:00
|
|
|
// Make sure SMAP protection is enabled on syscall entry.
|
|
|
|
clac();
|
|
|
|
|
2020-01-01 23:10:25 +01:00
|
|
|
// Apply a random offset in the range 0-255 to the stack pointer,
|
|
|
|
// to make kernel stacks a bit less deterministic.
|
2020-01-03 12:48:28 +01:00
|
|
|
auto* ptr = (char*)__builtin_alloca(get_fast_random<u8>());
|
2020-01-01 23:10:25 +01:00
|
|
|
asm volatile(""
|
|
|
|
: "=m"(*ptr));
|
|
|
|
|
2019-11-09 22:18:16 +01:00
|
|
|
auto& process = current->process();
|
2019-11-17 12:11:43 +01:00
|
|
|
|
2020-01-09 18:02:01 +01:00
|
|
|
if (!MM.validate_user_stack(process, VirtualAddress(regs.userspace_esp))) {
|
|
|
|
dbgprintf("Invalid stack pointer: %p\n", regs.userspace_esp);
|
2019-11-17 12:11:43 +01:00
|
|
|
handle_crash(regs, "Bad stack on syscall entry", SIGSTKFLT);
|
|
|
|
ASSERT_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
2019-11-29 16:15:30 +01:00
|
|
|
auto* calling_region = MM.region_from_vaddr(process, VirtualAddress(regs.eip));
|
|
|
|
if (!calling_region) {
|
|
|
|
dbgprintf("Syscall from %p which has no region\n", regs.eip);
|
|
|
|
handle_crash(regs, "Syscall from unknown region", SIGSEGV);
|
|
|
|
ASSERT_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (calling_region->is_writable()) {
|
|
|
|
dbgprintf("Syscall from writable memory at %p\n", regs.eip);
|
|
|
|
handle_crash(regs, "Syscall from writable memory", SIGSEGV);
|
|
|
|
ASSERT_NOT_REACHED();
|
|
|
|
}
|
|
|
|
|
2019-11-09 22:18:16 +01:00
|
|
|
process.big_lock().lock();
|
2019-07-03 21:17:35 +02:00
|
|
|
u32 function = regs.eax;
|
|
|
|
u32 arg1 = regs.edx;
|
|
|
|
u32 arg2 = regs.ecx;
|
|
|
|
u32 arg3 = regs.ebx;
|
2019-11-09 22:18:16 +01:00
|
|
|
regs.eax = (u32)Syscall::handle(regs, function, arg1, arg2, arg3);
|
|
|
|
if (auto* tracer = process.tracer())
|
2019-04-22 18:44:45 +02:00
|
|
|
tracer->did_syscall(function, arg1, arg2, arg3, regs.eax);
|
2019-11-09 22:18:16 +01:00
|
|
|
process.big_lock().unlock();
|
Kernel: Unwind kernel stacks before dying
While executing in the kernel, a thread can acquire various resources
that need cleanup, such as locks and references to RefCounted objects.
This cleanup normally happens on the exit path, such as in destructors
for various RAII guards. But we weren't calling those exit paths when
killing threads that have been executing in the kernel, such as threads
blocked on reading or sleeping, thus causing leaks.
This commit changes how killing threads works. Now, instead of killing
a thread directly, one is supposed to call thread->set_should_die(),
which will unblock it and make it unwind the stack if it is blocked
in the kernel. Then, just before returning to the userspace, the thread
will automatically die.
2019-11-14 18:46:01 +03:00
|
|
|
|
|
|
|
// Check if we're supposed to return to userspace or just die.
|
|
|
|
current->die_if_needed();
|
2018-10-16 11:01:38 +02:00
|
|
|
}
|