#include "Log.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/ELF.h" #include "thread/Scheduler.h" #include "thread/ThreadImage.h" #include #include #include #include #include static Result> copy_string_vector_from_userspace(u64 address) { Vector result; const u64* user_vector = (const u64*)address; u64 string_addr; while (true) { if (!MemoryManager::copy_from_user_typed(user_vector, &string_addr)) return err(EFAULT); if (!string_addr) break; auto string = TRY(MemoryManager::strdup_from_user(string_addr)); TRY(result.try_append(move(string))); user_vector++; } return result; } static u64 calculate_userspace_stack_size(const Vector& v) { u64 total { 0 }; for (const auto& str : v) { // The string's byte count + a terminating NUL byte. total += str.length() + 1; // The pointer to said string in the userspace array. total += sizeof(char*); } // The NULL pointer at the end of the userspace array. total += sizeof(char*); return total; } static constexpr usize MAX_ARGV_STACK_SIZE = 2 * ARCH_PAGE_SIZE; Result sys_execve(Registers* regs, SyscallArgs args) { auto path = TRY(MemoryManager::strdup_from_user(args[0])); auto argv = TRY(copy_string_vector_from_userspace(args[1])); auto envp = TRY(copy_string_vector_from_userspace(args[2])); if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE) return err(E2BIG); auto current = Scheduler::current(); auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory)); if (!VFS::can_execute(inode, current->auth)) return err(EACCES); #ifdef EXEC_DEBUG kdbgln("exec: attempting to replace current image with %s", path.chars()); #endif auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); }); auto image = TRY(ThreadImage::try_load_from_elf(inode)); u64 user_argv = TRY(image->push_string_vector_on_stack(argv)); usize user_argc = argv.size(); u64 user_envp = TRY(image->push_string_vector_on_stack(envp)); usize user_envc = envp.size(); // From now on, nothing should fail. #ifdef EXEC_DEBUG kdbgln("exec: image load ok, will now replace existing process image"); #endif guard.deactivate(); for (int i = 0; i < FD_MAX; i++) { auto& descriptor = current->fd_table[i]; if (!descriptor.has_value()) continue; if (descriptor->flags & O_CLOEXEC) { descriptor->inode->remove_handle(); descriptor = {}; } } MMU::delete_userspace_page_directory(current->directory); if (VFS::is_setuid(inode)) current->auth.euid = current->auth.suid = inode->uid(); if (VFS::is_setgid(inode)) current->auth.egid = current->auth.sgid = inode->gid(); current->name = path.chars(); image->apply(current); MMU::switch_page_directory(current->directory); current->set_arguments(user_argc, user_argv, user_envc, user_envp); memcpy(regs, ¤t->regs, sizeof(*regs)); kinfoln("exec: thread %lu was replaced with %s", current->id, path.chars()); return 0; } Result sys_fork(Registers* regs, SyscallArgs) { auto current = Scheduler::current(); auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); }); memcpy(¤t->regs, regs, sizeof(*regs)); auto current_directory_path = TRY(current->current_directory_path.clone()); auto image = TRY(ThreadImage::clone_from_thread(current)); auto thread = TRY(new_thread()); thread->state = ThreadState::Runnable; thread->is_kernel = false; thread->fp_data.save(); thread->name = current->name; thread->auth = current->auth; thread->current_directory = current->current_directory; thread->current_directory_path = move(current_directory_path); thread->umask = current->umask; thread->parent = current; for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; if (current->fd_table[i].has_value()) current->fd_table[i]->inode->add_handle(); } image->apply(thread); memcpy(&thread->regs, regs, sizeof(*regs)); thread->set_return(0); Scheduler::add_thread(thread); #ifdef FORK_DEBUG kdbgln("fork: thread %lu forked into child %lu", current->id, thread->id); #endif return thread->id; }