kernel+libc: Add pledge support
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
0ae409ae22
commit
e2a77bb3da
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
66
kernel/src/Pledge.cpp
Normal file
66
kernel/src/Pledge.cpp
Normal file
@ -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<void> 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<int> 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;
|
||||
}
|
19
kernel/src/Pledge.h
Normal file
19
kernel/src/Pledge.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "thread/Thread.h"
|
||||
#include <luna/Result.h>
|
||||
|
||||
#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<void> check_pledge(Thread* thread, Promise promise);
|
||||
|
||||
Result<int> parse_promises(u64 pledge);
|
@ -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<u64> ConsoleDevice::ioctl(int request, void* arg)
|
||||
{
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_tty));
|
||||
|
||||
switch (request)
|
||||
{
|
||||
case TCGETS: {
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
@ -7,6 +8,8 @@ Result<u64> 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;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -9,6 +10,8 @@ Result<u64> sys_chdir(Registers*, SyscallArgs args)
|
||||
|
||||
Thread* current = Scheduler::current();
|
||||
|
||||
TRY(check_pledge(current, Promise::p_rpath));
|
||||
|
||||
if (PathParser::is_absolute(path.view()))
|
||||
{
|
||||
SharedPtr<VFS::Inode> inode = TRY(VFS::resolve_path(path.chars(), current->auth));
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "Pledge.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <bits/clockid.h>
|
||||
#include <bits/timespec.h>
|
||||
|
||||
@ -9,6 +11,10 @@ Result<u64> 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: {
|
||||
|
@ -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<u64> 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<u64> 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<u64> 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<u64> 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]; }
|
||||
|
||||
|
@ -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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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];
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "fs/VFS.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
@ -11,6 +12,7 @@ Result<u64> 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));
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -5,41 +6,54 @@
|
||||
|
||||
Result<u64> sys_getpid(Registers*, SyscallArgs)
|
||||
{
|
||||
return Scheduler::current()->id;
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_stdio));
|
||||
return current->id;
|
||||
}
|
||||
|
||||
Result<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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<u64> 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)));
|
||||
|
||||
|
@ -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<u64> 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<u64> 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<u64> 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<u64> 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()));
|
||||
|
||||
|
@ -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<u64> 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));
|
||||
|
@ -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<u64> 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<OpenFileDescription> description;
|
||||
if ((params.flags & MAP_ANONYMOUS) != MAP_ANONYMOUS)
|
||||
@ -94,6 +97,7 @@ Result<u64> sys_munmap(Registers*, SyscallArgs args)
|
||||
if (!is_aligned<ARCH_PAGE_SIZE>(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<u64> sys_msync(Registers*, SyscallArgs args)
|
||||
if (!is_aligned<ARCH_PAGE_SIZE>(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)));
|
||||
|
||||
|
@ -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<u64> 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<SharedPtr<Device>> {
|
||||
@ -44,6 +46,7 @@ Result<u64> 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<u64> 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));
|
||||
|
@ -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<u64> 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<u64> 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<FileDescriptor>& descriptor = current->fd_table[fd];
|
||||
|
||||
|
40
kernel/src/sys/pledge.cpp
Normal file
40
kernel/src/sys/pledge.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "Pledge.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
|
||||
Result<u64> 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;
|
||||
}
|
@ -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<u64> sys_poll(Registers*, SyscallArgs args)
|
||||
|
||||
Vector<SharedPtr<VFS::Inode>> inodes;
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_stdio));
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++)
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -14,6 +15,9 @@ Result<u64> 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;
|
||||
|
||||
|
@ -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<u64> sys_sigreturn(Registers* regs, SyscallArgs)
|
||||
Result<u64> 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<u64> sys_sigaction(Registers*, SyscallArgs args)
|
||||
Result<u64> 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<u64> sys_kill(Registers*, SyscallArgs args)
|
||||
Result<u64> 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];
|
||||
|
@ -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<u64> sys_socket(Registers*, SyscallArgs args)
|
||||
if (type != SOCK_STREAM) return err(EPROTOTYPE);
|
||||
if (domain != AF_UNIX) return err(EAFNOSUPPORT);
|
||||
|
||||
auto socket = TRY(make_shared<UnixSocket>());
|
||||
|
||||
auto* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_unix));
|
||||
|
||||
auto socket = TRY(make_shared<UnixSocket>());
|
||||
|
||||
int fd = TRY(current->allocate_fd(0));
|
||||
|
||||
@ -36,6 +38,7 @@ Result<u64> 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<u64> 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<u64> 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<u64> 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;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
@ -33,6 +34,7 @@ Result<u64> 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<u64> 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
|
||||
|
@ -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<u64> 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);
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Pledge.h"
|
||||
#include "sys/Syscall.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include <sys/types.h>
|
||||
@ -6,12 +7,13 @@ Result<u64> 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;
|
||||
}
|
||||
|
@ -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<u64> sys_waitpid(Registers* regs, SyscallArgs args)
|
||||
int options = (int)args[2];
|
||||
|
||||
Thread* current = Scheduler::current();
|
||||
TRY(check_pledge(current, Promise::p_stdio));
|
||||
|
||||
Thread* thread;
|
||||
|
||||
|
@ -89,6 +89,9 @@ struct Thread : public LinkedListNode<Thread>
|
||||
u64 sleep_ticks_left;
|
||||
u64 alarm_ticks_left { 0 };
|
||||
|
||||
int promises { -1 };
|
||||
int execpromises { -1 };
|
||||
|
||||
Stack stack;
|
||||
Stack kernel_stack;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user