diff --git a/apps/shmem-test.cpp b/apps/shmem-test.cpp index 968b335b..4e9db300 100644 --- a/apps/shmem-test.cpp +++ b/apps/shmem-test.cpp @@ -6,6 +6,8 @@ int main() { + pledge("stdio rpath wpath cpath proc", nullptr); + int fd = shm_open("/shared", O_CREAT | O_RDWR, 0666); if (fd < 0) { diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c77e1236..1fd13dec 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES ${HEADERS} src/main.cpp src/Log.cpp + src/Pledge.cpp src/cxxabi.cpp src/video/Framebuffer.cpp src/video/TextConsole.cpp @@ -45,6 +46,7 @@ set(SOURCES src/sys/socket.cpp src/sys/poll.cpp src/sys/alarm.cpp + src/sys/pledge.cpp src/fs/VFS.cpp src/fs/Pipe.cpp src/fs/Mount.cpp diff --git a/kernel/src/Log.cpp b/kernel/src/Log.cpp index b2dbdab1..9d2e3055 100644 --- a/kernel/src/Log.cpp +++ b/kernel/src/Log.cpp @@ -27,8 +27,8 @@ static void log_serial(LogLevel level, const char* format, va_list origin) va_list ap; va_copy(ap, origin); - const SafeScopeLock lock { g_serial_lock }; - if (!lock.did_succeed()) return; + /*const SafeScopeLock lock { g_serial_lock }; + if (!lock.did_succeed()) return;*/ Serial::printf("\x1b[%sm" "%c" @@ -61,7 +61,7 @@ static void log_text_console(LogLevel level, const char* format, va_list origin) va_list ap; va_copy(ap, origin); - const ScopeLock lock { g_console_lock }; + /*const ScopeLock lock { g_console_lock };*/ const u32 original_foreground = TextConsole::foreground(); const u32 original_background = TextConsole::background(); diff --git a/kernel/src/Pledge.cpp b/kernel/src/Pledge.cpp new file mode 100644 index 00000000..0ecc0ed2 --- /dev/null +++ b/kernel/src/Pledge.cpp @@ -0,0 +1,66 @@ +#include "Pledge.h" +#include "Log.h" +#include "memory/MemoryManager.h" + +static const char* promise_names[] = { +#define __enumerate(promise) #promise, + enumerate_promises(__enumerate) +#undef __enumerate +}; + +Result check_pledge(Thread* thread, Promise promise) +{ + // Thread has not called pledge(). + if (thread->promises < 0) return {}; + int mask = (1 << (int)promise); + if ((thread->promises & mask) != mask) + { + kerrorln("Pledge violation in thread %d! Has not pledged %s", thread->id, promise_names[(int)promise]); + if (thread->promises & (1 << (int)Promise::p_error)) return err(ENOSYS); + + // Kill this thread with an uncatchable SIGABRT. For this, we reset the disposition of SIGABRT to the default + // (dump core). We could just kill the thread here and be done, but that discards anything on the current stack, + // which means that some destructors might not be called. Instead, leave the job to the next call of + // Thread::process_pending_signals(). + thread->signal_handlers[SIGABRT - 1].sa_handler = SIG_DFL; + + // If there are any other pending signals, they might be processed before SIGABRT. Avoid that by resetting the + // thread's pending signals. + thread->pending_signals = 0; + + thread->send_signal(SIGABRT); + + // This should never arrive to userspace. + return err(ENOSYS); + } + + return {}; +} + +Result parse_promises(u64 pledge) +{ + if (!pledge) return -1; + + auto text = TRY(MemoryManager::strdup_from_user(pledge)); + if (text.is_empty()) return 0; + + auto promises = TRY(text.split(" ")); + + int result = 0; + for (const auto& promise : promises) + { + for (int i = 0; i < (int)Promise::num_promises; i++) + { + if (promise.view() == promise_names[i]) + { + result |= (1 << i); + goto found; + } + } + return err(EINVAL); + found: + continue; + } + + return result; +} diff --git a/kernel/src/Pledge.h b/kernel/src/Pledge.h new file mode 100644 index 00000000..8b9bdba0 --- /dev/null +++ b/kernel/src/Pledge.h @@ -0,0 +1,19 @@ +#pragma once +#include "thread/Thread.h" +#include + +#define enumerate_promises(_p) \ + _p(stdio) _p(rpath) _p(wpath) _p(cpath) _p(fattr) _p(chown) _p(unix) _p(tty) _p(proc) _p(exec) _p(prot_exec) \ + _p(id) _p(mount) _p(signal) _p(host) _p(error) + +enum class Promise +{ +#define __enumerate(promise) p_##promise, + enumerate_promises(__enumerate) +#undef __enumerate + num_promises, +}; + +Result check_pledge(Thread* thread, Promise promise); + +Result parse_promises(u64 pledge); diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp index 9e21cb86..b2c78411 100644 --- a/kernel/src/fs/devices/ConsoleDevice.cpp +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -1,5 +1,6 @@ #include "fs/devices/ConsoleDevice.h" #include "Log.h" +#include "Pledge.h" #include "fs/devices/DeviceRegistry.h" #include "fs/devices/KeyboardDevice.h" #include "memory/MemoryManager.h" @@ -199,6 +200,9 @@ void ConsoleDevice::process_key_event(u8 scancode) Result ConsoleDevice::ioctl(int request, void* arg) { + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_tty)); + switch (request) { case TCGETS: { diff --git a/kernel/src/sys/alarm.cpp b/kernel/src/sys/alarm.cpp index c4f8d7d8..a0fe0e4e 100644 --- a/kernel/src/sys/alarm.cpp +++ b/kernel/src/sys/alarm.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -7,6 +8,8 @@ Result sys_alarm(Registers*, SyscallArgs args) auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + u64 time_left = current->alarm_ticks_left; current->alarm_ticks_left = seconds * 1000; diff --git a/kernel/src/sys/chdir.cpp b/kernel/src/sys/chdir.cpp index 1bdd8a17..7523a889 100644 --- a/kernel/src/sys/chdir.cpp +++ b/kernel/src/sys/chdir.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -9,6 +10,8 @@ Result sys_chdir(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_rpath)); + if (PathParser::is_absolute(path.view())) { SharedPtr inode = TRY(VFS::resolve_path(path.chars(), current->auth)); diff --git a/kernel/src/sys/clock_gettime.cpp b/kernel/src/sys/clock_gettime.cpp index d8083a5c..09bfd163 100644 --- a/kernel/src/sys/clock_gettime.cpp +++ b/kernel/src/sys/clock_gettime.cpp @@ -1,6 +1,8 @@ +#include "Pledge.h" #include "arch/Timer.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" +#include "thread/Scheduler.h" #include #include @@ -9,6 +11,10 @@ Result sys_clock_gettime(Registers*, SyscallArgs args) clockid_t id = (clockid_t)args[0]; struct timespec* ts = (struct timespec*)args[1]; + auto* current = Scheduler::current(); + + TRY(check_pledge(current, Promise::p_stdio)); + switch (id) { case CLOCK_MONOTONIC: { diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 724ea934..d83fe89a 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "binfmt/BinaryFormat.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" @@ -63,6 +64,8 @@ Result sys_execve(Registers* regs, SyscallArgs args) auto current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_exec)); + auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory)); if (!VFS::can_execute(inode, current->auth)) return err(EACCES); @@ -115,6 +118,9 @@ Result sys_execve(Registers* regs, SyscallArgs args) current->set_arguments(user_argc, user_argv, user_envc, user_envp); + current->promises = current->execpromises; + current->execpromises = -1; + memcpy(regs, ¤t->regs, sizeof(*regs)); for (int i = 0; i < NSIG; i++) @@ -133,6 +139,8 @@ Result sys_fork(Registers* regs, SyscallArgs) { auto current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_proc)); + auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory()); }); memcpy(¤t->regs, regs, sizeof(*regs)); @@ -152,6 +160,10 @@ Result sys_fork(Registers* regs, SyscallArgs) thread->current_directory_path = move(current_directory_path); thread->umask = current->umask; thread->parent = current; + // TODO: Should promises be inherited across fork()? We're assuming yes, as they're already reset on exec (unless + // execpromises has been set). Couldn't find any suitable documentation from OpenBSD about this. + thread->promises = current->promises; + thread->execpromises = current->execpromises; for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; } diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index a49b70d7..5de0152f 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "fs/Pipe.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" @@ -24,6 +25,8 @@ Result sys_read(Registers* regs, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + auto& descriptor = *TRY(current->resolve_fd(fd)); if (!descriptor.is_readable()) return err(EBADF); @@ -65,6 +68,8 @@ Result sys_write(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + auto& descriptor = *TRY(current->resolve_fd(fd)); if (!descriptor.is_writable()) return err(EBADF); @@ -87,6 +92,8 @@ Result sys_lseek(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + auto& descriptor = *TRY(current->resolve_fd(fd)); if (descriptor.inode()->type() == VFS::InodeType::FIFO) return err(ESPIPE); @@ -117,6 +124,8 @@ Result sys_fcntl(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + auto& descriptor = *TRY(current->resolve_fd(fd)); bool is_cloexec = true; @@ -176,6 +185,7 @@ Result sys_isatty(Registers*, SyscallArgs args) int fd = (int)args[0]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); auto& descriptor = *TRY(current->resolve_fd(fd)); return descriptor.inode()->isatty(); @@ -188,6 +198,8 @@ Result sys_dup2(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + if (newfd < 0 || newfd >= FD_MAX) return err(EBADF); auto descriptor = *TRY(current->resolve_fd(oldfd)); @@ -206,6 +218,8 @@ Result sys_pipe(Registers*, SyscallArgs args) Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + int rfd = TRY(current->allocate_fd(0)); int wfd = TRY(current->allocate_fd(rfd + 1)); @@ -229,6 +243,8 @@ Result sys_umask(Registers*, SyscallArgs args) auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + mode_t old_umask = current->umask; current->umask = new_umask & 0777; @@ -242,6 +258,7 @@ Result sys_truncate(Registers*, SyscallArgs args) size_t length = (size_t)args[1]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_wpath)); auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory)); if (!VFS::can_write(inode, current->auth)) return err(EACCES); @@ -257,6 +274,7 @@ Result sys_ftruncate(Registers*, SyscallArgs args) size_t length = (size_t)args[1]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); auto description = TRY(current->resolve_fd(fd))->description; if (!(description->flags & O_WRONLY)) return err(EBADF); @@ -273,6 +291,7 @@ Result sys_utimensat(Registers*, SyscallArgs args) int flags = (int)args[3]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_fattr)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW))); struct timespec ktimes[2]; diff --git a/kernel/src/sys/getdents.cpp b/kernel/src/sys/getdents.cpp index 0f5fa777..3e268d0e 100644 --- a/kernel/src/sys/getdents.cpp +++ b/kernel/src/sys/getdents.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -11,6 +12,7 @@ Result sys_getdents(Registers*, SyscallArgs args) usize count = (usize)args[2]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); auto& descriptor = *TRY(current->resolve_fd(fd)); diff --git a/kernel/src/sys/id.cpp b/kernel/src/sys/id.cpp index e6b116e4..383745a0 100644 --- a/kernel/src/sys/id.cpp +++ b/kernel/src/sys/id.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -5,41 +6,54 @@ Result sys_getpid(Registers*, SyscallArgs) { - return Scheduler::current()->id; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + return current->id; } Result sys_getppid(Registers*, SyscallArgs) { - auto* parent = Scheduler::current()->parent; - + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + auto* parent = current->parent; return parent ? parent->id : 0; } Result sys_getuid(Registers*, SyscallArgs) { - return Scheduler::current()->auth.uid; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + return current->auth.uid; } Result sys_geteuid(Registers*, SyscallArgs) { - return Scheduler::current()->auth.euid; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + return current->auth.euid; } Result sys_getgid(Registers*, SyscallArgs) { - return Scheduler::current()->auth.gid; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + return current->auth.gid; } Result sys_getegid(Registers*, SyscallArgs) { - return Scheduler::current()->auth.egid; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + return current->auth.egid; } Result sys_setuid(Registers*, SyscallArgs args) { u32 uid = (u32)args[0]; - Credentials& auth = Scheduler::current()->auth; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_id)); + Credentials& auth = current->auth; if (auth.euid == 0) { @@ -57,7 +71,9 @@ Result sys_seteuid(Registers*, SyscallArgs args) { u32 uid = (u32)args[0]; - Credentials& auth = Scheduler::current()->auth; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_id)); + Credentials& auth = current->auth; if (auth.euid != 0 && uid != auth.uid && uid != auth.suid) return err(EPERM); auth.euid = uid; @@ -69,7 +85,9 @@ Result sys_setgid(Registers*, SyscallArgs args) { u32 gid = (u32)args[0]; - Credentials& auth = Scheduler::current()->auth; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_id)); + Credentials& auth = current->auth; if (auth.euid == 0) { @@ -87,7 +105,9 @@ Result sys_setegid(Registers*, SyscallArgs args) { u32 gid = (u32)args[0]; - Credentials& auth = Scheduler::current()->auth; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_id)); + Credentials& auth = current->auth; if (auth.euid != 0 && gid != auth.gid && gid != auth.sgid) return err(EPERM); auth.egid = gid; @@ -101,6 +121,7 @@ Result sys_setpgid(Registers*, SyscallArgs args) pid_t pgid = (pid_t)args[1]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_proc)); if (pid == 0) pid = current->id; if (pgid == 0) pgid = current->id; @@ -133,6 +154,7 @@ Result sys_getpgid(Registers*, SyscallArgs args) pid_t pid = (pid_t)args[0]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); if (pid == 0) pid = current->id; if (pid < 0) return err(EINVAL); @@ -150,6 +172,7 @@ Result sys_fchmodat(Registers*, SyscallArgs args) int flags = (int)args[3]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_wpath)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW))); @@ -171,6 +194,7 @@ Result sys_fchownat(Registers*, SyscallArgs args) int flags = (int)args[4]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_chown)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW))); diff --git a/kernel/src/sys/link.cpp b/kernel/src/sys/link.cpp index 813caf9a..e7e36057 100644 --- a/kernel/src/sys/link.cpp +++ b/kernel/src/sys/link.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -12,6 +13,7 @@ Result sys_unlinkat(Registers*, SyscallArgs args) int flags = (int)args[2]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_cpath)); auto dirname = TRY(PathParser::dirname(path.view())); auto basename = TRY(PathParser::basename(path.view())); @@ -44,6 +46,7 @@ Result sys_symlinkat(Registers*, SyscallArgs args) if (target.is_empty()) return err(ENOENT); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_cpath)); auto parent = TRY(PathParser::dirname(linkpath.view())); @@ -73,6 +76,7 @@ Result sys_readlinkat(Registers*, SyscallArgs args) usize bufsiz = (usize)args[3]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_rpath)); auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false)); @@ -98,6 +102,7 @@ Result sys_linkat(Registers*, SyscallArgs args) int flags = (int)args[4]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_cpath)); auto parent = TRY(PathParser::dirname(newpath.view())); diff --git a/kernel/src/sys/mkdir.cpp b/kernel/src/sys/mkdir.cpp index acebd414..69a6da36 100644 --- a/kernel/src/sys/mkdir.cpp +++ b/kernel/src/sys/mkdir.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -10,6 +11,7 @@ Result sys_mkdir(Registers*, SyscallArgs args) mode_t mode = (mode_t)args[1]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_cpath)); auto inode = TRY(VFS::create_directory(path.chars(), mode & ~current->umask, current->auth, current->current_directory)); diff --git a/kernel/src/sys/mmap.cpp b/kernel/src/sys/mmap.cpp index 9d9f2566..4e3be81e 100644 --- a/kernel/src/sys/mmap.cpp +++ b/kernel/src/sys/mmap.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "arch/MMU.h" #include "memory/MemoryManager.h" #include "memory/SharedMemory.h" @@ -22,6 +23,8 @@ Result sys_mmap(Registers*, SyscallArgs args) if (params.flags < 0) return err(EINVAL); Thread* current = Scheduler::current(); + if (params.prot & PROT_EXEC) TRY(check_pledge(current, Promise::p_prot_exec)); + TRY(check_pledge(current, Promise::p_stdio)); SharedPtr description; if ((params.flags & MAP_ANONYMOUS) != MAP_ANONYMOUS) @@ -94,6 +97,7 @@ Result sys_munmap(Registers*, SyscallArgs args) if (!is_aligned(address)) return err(EINVAL); Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); bool ok = TRY(current->address_space->free_region(address, ceil_div(size, ARCH_PAGE_SIZE))); @@ -118,6 +122,7 @@ Result sys_msync(Registers*, SyscallArgs args) if (!is_aligned(address)) return err(EINVAL); Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); TRY(current->address_space->sync_regions(address, ceil_div(size, ARCH_PAGE_SIZE))); diff --git a/kernel/src/sys/mount.cpp b/kernel/src/sys/mount.cpp index 3071283e..b01512e2 100644 --- a/kernel/src/sys/mount.cpp +++ b/kernel/src/sys/mount.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "fs/VFS.h" #include "fs/ext2/FileSystem.h" #include "fs/tmpfs/FileSystem.h" @@ -12,6 +13,7 @@ Result sys_mount(Registers*, SyscallArgs args) auto source = TRY(MemoryManager::strdup_from_user(args[2])); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_mount)); if (current->auth.euid != 0) return err(EPERM); auto get_source = [current, &source]() -> Result> { @@ -44,6 +46,7 @@ Result sys_umount(Registers*, SyscallArgs args) auto target = TRY(MemoryManager::strdup_from_user(args[0])); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_mount)); if (current->auth.euid != 0) return err(EPERM); TRY(VFS::umount(target.chars(), current->auth, current->current_directory)); @@ -57,6 +60,7 @@ Result sys_pivot_root(Registers*, SyscallArgs args) auto put_old = TRY(MemoryManager::strdup_from_user(args[1])); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_mount)); if (current->auth.euid != 0) return err(EPERM); TRY(VFS::pivot_root(new_root.chars(), put_old.chars(), current->current_directory)); diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index cec57de4..90d5d84a 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -23,6 +24,12 @@ Result sys_openat(Registers*, SyscallArgs args) // Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR if ((flags & O_RDWR) == 0) { return err(EINVAL); } + if (flags & O_RDONLY) TRY(check_pledge(current, Promise::p_rpath)); + else if (flags & O_WRONLY) + TRY(check_pledge(current, Promise::p_wpath)); + + if (flags & O_CREAT) TRY(check_pledge(current, Promise::p_cpath)); + if (flags & O_TMPFILE) { if (!(flags & O_WRONLY)) return err(EINVAL); @@ -99,6 +106,7 @@ Result sys_close(Registers*, SyscallArgs args) if (fd < 0 || fd >= FD_MAX) return err(EBADF); Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); Option& descriptor = current->fd_table[fd]; diff --git a/kernel/src/sys/pledge.cpp b/kernel/src/sys/pledge.cpp new file mode 100644 index 00000000..cec37ef7 --- /dev/null +++ b/kernel/src/sys/pledge.cpp @@ -0,0 +1,40 @@ +#include "Pledge.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" + +Result sys_pledge(Registers*, SyscallArgs args) +{ + int promises = TRY(parse_promises(args[0])); + int execpromises = TRY(parse_promises(args[1])); + + auto* current = Scheduler::current(); + + if (promises >= 0) + { + int actual_promises = promises & ~(1 << (int)Promise::p_error); + int old_promises = current->promises & ~(1 << (int)Promise::p_error); + + if (actual_promises & ~old_promises) + { + if (current->promises & ~(1 << (int)Promise::p_error)) return 0; + return err(EPERM); + } + } + + if (execpromises >= 0) + { + int actual_execpromises = execpromises & ~(1 << (int)Promise::p_error); + int old_execpromises = current->execpromises & ~(1 << (int)Promise::p_error); + + if (actual_execpromises & ~old_execpromises) + { + if (current->execpromises & ~(1 << (int)Promise::p_error)) return 0; + return err(EPERM); + } + } + + if (promises >= 0) current->promises = promises; + if (execpromises >= 0) current->execpromises = execpromises; + + return 0; +} diff --git a/kernel/src/sys/poll.cpp b/kernel/src/sys/poll.cpp index 973d2f1a..3283098f 100644 --- a/kernel/src/sys/poll.cpp +++ b/kernel/src/sys/poll.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "fs/VFS.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -18,6 +19,7 @@ Result sys_poll(Registers*, SyscallArgs args) Vector> inodes; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); for (nfds_t i = 0; i < nfds; i++) { diff --git a/kernel/src/sys/pstat.cpp b/kernel/src/sys/pstat.cpp index b930f173..dbb7947d 100644 --- a/kernel/src/sys/pstat.cpp +++ b/kernel/src/sys/pstat.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -14,6 +15,9 @@ Result sys_pstat(Registers*, SyscallArgs args) pid_t pid = (pid_t)args[0]; struct process* ps = (struct process*)args[1]; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_proc)); + // If pid == -1, return the PID of the last spawned thread. if (pid == -1) return g_threads.expect_last()->id; diff --git a/kernel/src/sys/signal.cpp b/kernel/src/sys/signal.cpp index 2102ce75..112cd458 100644 --- a/kernel/src/sys/signal.cpp +++ b/kernel/src/sys/signal.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -18,6 +19,7 @@ Result sys_sigreturn(Registers* regs, SyscallArgs) Result sys_sigaction(Registers*, SyscallArgs args) { auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_signal)); int signo = (int)args[0]; const struct sigaction* act = (const struct sigaction*)args[1]; @@ -47,6 +49,7 @@ Result sys_sigaction(Registers*, SyscallArgs args) Result sys_kill(Registers*, SyscallArgs args) { auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_proc)); pid_t pid = (pid_t)args[0]; int signo = (int)args[1]; @@ -68,6 +71,7 @@ Result sys_kill(Registers*, SyscallArgs args) Result sys_sigprocmask(Registers*, SyscallArgs args) { auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_signal)); int how = (int)args[0]; const sigset_t* set = (const sigset_t*)args[1]; diff --git a/kernel/src/sys/socket.cpp b/kernel/src/sys/socket.cpp index 2132430b..908575d8 100644 --- a/kernel/src/sys/socket.cpp +++ b/kernel/src/sys/socket.cpp @@ -1,4 +1,5 @@ #include "net/Socket.h" +#include "Pledge.h" #include "memory/MemoryManager.h" #include "net/UnixSocket.h" #include "sys/Syscall.h" @@ -14,9 +15,10 @@ Result sys_socket(Registers*, SyscallArgs args) if (type != SOCK_STREAM) return err(EPROTOTYPE); if (domain != AF_UNIX) return err(EAFNOSUPPORT); - auto socket = TRY(make_shared()); - auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_unix)); + + auto socket = TRY(make_shared()); int fd = TRY(current->allocate_fd(0)); @@ -36,6 +38,7 @@ Result sys_bind(Registers*, SyscallArgs args) if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_unix)); auto inode = TRY(current->resolve_fd(sockfd))->inode(); @@ -59,6 +62,7 @@ Result sys_connect(Registers* regs, SyscallArgs args) if (!MemoryManager::copy_from_user(addr, &storage, addrlen)) return err(EFAULT); auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_unix)); auto description = TRY(current->resolve_fd(sockfd))->description; @@ -77,6 +81,7 @@ Result sys_listen(Registers*, SyscallArgs args) int backlog = (int)args[1]; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_unix)); auto inode = TRY(current->resolve_fd(sockfd))->inode(); @@ -104,6 +109,7 @@ Result sys_accept(Registers* regs, SyscallArgs args) } auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_unix)); auto description = TRY(current->resolve_fd(sockfd))->description; diff --git a/kernel/src/sys/stat.cpp b/kernel/src/sys/stat.cpp index cd33fc7a..8a2806a7 100644 --- a/kernel/src/sys/stat.cpp +++ b/kernel/src/sys/stat.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -33,6 +34,7 @@ Result sys_fstatat(Registers*, SyscallArgs args) int flags = (int)args[3]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_rpath)); auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH, !(flags & AT_SYMLINK_NOFOLLOW))); @@ -67,6 +69,7 @@ Result sys_faccessat(Registers*, SyscallArgs args) Credentials creds; auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_rpath)); if (flags & AT_EACCESS) creds = current->auth; else diff --git a/kernel/src/sys/uname.cpp b/kernel/src/sys/uname.cpp index 59387a7c..28e4785c 100644 --- a/kernel/src/sys/uname.cpp +++ b/kernel/src/sys/uname.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "arch/CPU.h" #include "config.h" #include "memory/MemoryManager.h" @@ -40,6 +41,7 @@ Result sys_sethostname(Registers*, SyscallArgs args) usize length = (usize)args[1]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_host)); if (current->auth.euid != 0) return err(EPERM); if (length >= _UTSNAME_LENGTH) return err(EINVAL); diff --git a/kernel/src/sys/usleep.cpp b/kernel/src/sys/usleep.cpp index 06d1a1d4..8a16c50d 100644 --- a/kernel/src/sys/usleep.cpp +++ b/kernel/src/sys/usleep.cpp @@ -1,3 +1,4 @@ +#include "Pledge.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" #include @@ -6,12 +7,13 @@ Result sys_usleep(Registers*, SyscallArgs args) { useconds_t us = (useconds_t)args[0]; + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + // FIXME: Allow usleep() to use a more precise resolution. if (us < 1000) return 0; kernel_sleep(us / 1000); - auto* current = Scheduler::current(); - return current->sleep_ticks_left; } diff --git a/kernel/src/sys/waitpid.cpp b/kernel/src/sys/waitpid.cpp index 88cbc996..a385e166 100644 --- a/kernel/src/sys/waitpid.cpp +++ b/kernel/src/sys/waitpid.cpp @@ -1,4 +1,5 @@ #include "Log.h" +#include "Pledge.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" @@ -11,6 +12,7 @@ Result sys_waitpid(Registers* regs, SyscallArgs args) int options = (int)args[2]; Thread* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); Thread* thread; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 78ee01d8..ef4f4252 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -89,6 +89,9 @@ struct Thread : public LinkedListNode u64 sleep_ticks_left; u64 alarm_ticks_left { 0 }; + int promises { -1 }; + int execpromises { -1 }; + Stack stack; Stack kernel_stack; diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 5ec8b5c2..aa7f5938 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -196,6 +196,9 @@ extern "C" /* Schedule an alarm signal. */ unsigned int alarm(unsigned int seconds); + /* Restrict system operations. */ + int pledge(const char* promises, const char* execpromises); + #ifdef __cplusplus } #endif diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index e2501a51..460bf832 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -521,4 +521,10 @@ extern "C" { return (unsigned int)syscall(SYS_alarm, seconds); } + + int pledge(const char* promises, const char* execpromises) + { + long rc = syscall(SYS_pledge, promises, execpromises); + __errno_return(rc, int); + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index b49a05c8..d75b1ec4 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -8,7 +8,7 @@ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ - _e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) + _e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) enum Syscalls {