Skip to content

Commit

Permalink
Kernel: Move syscall precondition validates to MM
Browse files Browse the repository at this point in the history
Move these to MM to simplify the flow of the syscall handler.

While here, also make sure we hold the process space lock for
the duration of the validation to avoid potential issues where
another thread attempts to modify the process space during the
validation. This will allow us to move the validation out of the
big process lock scope in a future change.

Additionally utilize the new no_lock variants of functions to avoid
unnecessary recursive process space spinlock acquisitions.
  • Loading branch information
bgianfo authored and gunnarbeutner committed Jul 20, 2021
1 parent af54332 commit 27e1120
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 35 deletions.
34 changes: 1 addition & 33 deletions Kernel/Syscall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,39 +199,7 @@ NEVER_INLINE void syscall_handler(TrapFrame* trap)
// NOTE: We take the big process lock before inspecting memory regions.
process.big_lock().lock();

VirtualAddress userspace_sp;
#if ARCH(I386)
userspace_sp = VirtualAddress { regs.userspace_esp };
#else
userspace_sp = VirtualAddress { regs.userspace_rsp };
#endif
if (!MM.validate_user_stack(process.space(), userspace_sp)) {
dbgln("Invalid stack pointer: {:p}", userspace_sp);
handle_crash(regs, "Bad stack on syscall entry", SIGSTKFLT);
}

VirtualAddress ip;
#if ARCH(I386)
ip = VirtualAddress { regs.eip };
#else
ip = VirtualAddress { regs.rip };
#endif

auto* calling_region = MM.find_user_region_from_vaddr(process.space(), ip);
if (!calling_region) {
dbgln("Syscall from {:p} which has no associated region", ip);
handle_crash(regs, "Syscall from unknown region", SIGSEGV);
}

if (calling_region->is_writable()) {
dbgln("Syscall from writable memory at {:p}", ip);
handle_crash(regs, "Syscall from writable memory", SIGSEGV);
}

if (process.space().enforces_syscall_regions() && !calling_region->is_syscall_region()) {
dbgln("Syscall from non-syscall region");
handle_crash(regs, "Syscall from non-syscall region", SIGSEGV);
}
MM.validate_syscall_preconditions(process.space(), regs);

FlatPtr function;
FlatPtr arg1;
Expand Down
49 changes: 47 additions & 2 deletions Kernel/VM/MemoryManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,10 +624,55 @@ Region* MemoryManager::kernel_region_from_vaddr(VirtualAddress vaddr)
return nullptr;
}

Region* MemoryManager::find_user_region_from_vaddr_no_lock(Space& space, VirtualAddress vaddr)
{
VERIFY(space.get_lock().own_lock());
return space.find_region_containing({ vaddr, 1 });
}

Region* MemoryManager::find_user_region_from_vaddr(Space& space, VirtualAddress vaddr)
{
ScopedSpinLock lock(space.get_lock());
return space.find_region_containing({ vaddr, 1 });
return find_user_region_from_vaddr_no_lock(space, vaddr);
}

void MemoryManager::validate_syscall_preconditions(Space& space, RegisterState& regs)
{
// We take the space lock once here and then use the no_lock variants
// to avoid excessive spinlock recursion in this extemely common path.
ScopedSpinLock lock(space.get_lock());

auto unlock_and_handle_crash = [&lock, &regs](const char* description, int signal) {
lock.unlock();
handle_crash(regs, description, signal);
};

{
VirtualAddress userspace_sp = VirtualAddress { regs.userspace_sp() };
if (!MM.validate_user_stack_no_lock(space, userspace_sp)) {
dbgln("Invalid stack pointer: {:p}", userspace_sp);
unlock_and_handle_crash("Bad stack on syscall entry", SIGSTKFLT);
}
}

{
VirtualAddress ip = VirtualAddress { regs.ip() };
auto* calling_region = MM.find_user_region_from_vaddr_no_lock(space, ip);
if (!calling_region) {
dbgln("Syscall from {:p} which has no associated region", ip);
unlock_and_handle_crash("Syscall from unknown region", SIGSEGV);
}

if (calling_region->is_writable()) {
dbgln("Syscall from writable memory at {:p}", ip);
unlock_and_handle_crash("Syscall from writable memory", SIGSEGV);
}

if (space.enforces_syscall_regions() && !calling_region->is_syscall_region()) {
dbgln("Syscall from non-syscall region");
unlock_and_handle_crash("Syscall from non-syscall region", SIGSEGV);
}
}
}

Region* MemoryManager::find_region_from_vaddr(VirtualAddress vaddr)
Expand Down Expand Up @@ -1039,7 +1084,7 @@ bool MemoryManager::validate_user_stack_no_lock(Space& space, VirtualAddress vad
if (!is_user_address(vaddr))
return false;

auto* region = find_user_region_from_vaddr(space, vaddr);
auto* region = find_user_region_from_vaddr_no_lock(space, vaddr);
return region && region->is_user() && region->is_stack();
}

Expand Down
2 changes: 2 additions & 0 deletions Kernel/VM/MemoryManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ class MemoryManager {
}

static Region* find_user_region_from_vaddr(Space&, VirtualAddress);
static Region* find_user_region_from_vaddr_no_lock(Space&, VirtualAddress);
static void validate_syscall_preconditions(Space&, RegisterState&);

void dump_kernel_regions();

Expand Down

0 comments on commit 27e1120

Please sign in to comment.