{check,swallow}_error: lift out of rtext into errors::ext

the ::ext namespace is required to prevent a circular dep within the std lib
This commit is contained in:
2024-08-30 12:00:43 +00:00
parent d2bf8dbdbb
commit dab05a0f9c
4 changed files with 28 additions and 25 deletions

View File

@@ -4,7 +4,7 @@ use log;
use os::exec;
use rt;
export fn check_error(context: str, what: (void | fs::error | os::exec::error | rt::errno)) void = {
export fn check(context: str, what: (void | fs::error | os::exec::error | rt::errno)) void = {
match (what) {
case let e: fs::error => log::fatalf("{}: {}", context, fs::strerror(e));
case let e: os::exec::error => log::fatalf("{}: {}", context, os::exec::strerror(e));
@@ -13,7 +13,7 @@ export fn check_error(context: str, what: (void | fs::error | os::exec::error |
};
};
export fn swallow_error(context: str, what: (void | fs::error | os::exec::error | rt::errno)) void = {
export fn swallow(context: str, what: (void | fs::error | os::exec::error | rt::errno)) void = {
match (what) {
// N.B.: use `println(...)` over `printfln("{}: {}")` so that my treelogger
// can extract the context.

View File

@@ -1,5 +1,6 @@
// vim: set shiftwidth=2 :
use config;
use errors::ext;
use log;
use log::tree;
use restrict;
@@ -17,7 +18,7 @@ fn do_exec(path: str, args: []str) (os::exec::error | void) = {
log::printfln("exec ({}): {}", path, joined);
};
rtext::check_error("exec", rtext::execve(path, args, os::getenvs()));
errors::ext::check("exec", rtext::execve(path, args, os::getenvs()));
// XXX: os::exec::exec offers no way to preserve argv0, but it does
// work if you don't care about that:
@@ -50,5 +51,5 @@ export fn main() void = {
// XXX: landlock prevents other sandboxers like `bwrap` from executing,
// because it forbids all future `mount` syscalls. so don't landlock.
// restrict::landlock_restrict(&req.resources);
rtext::check_error("exec <user command>", do_exec(req.exec_bin, req.exec_args));
errors::ext::check("exec <user command>", do_exec(req.exec_bin, req.exec_args));
};

View File

@@ -1,4 +1,5 @@
// vim: set shiftwidth=2 :
use errors::ext;
use fs;
use log;
use os;
@@ -89,7 +90,7 @@ export fn landlock_restrict(what: *resources) void = {
for (let pathbuf .. what.paths) {
let pathstr = path::string(&pathbuf);
log::printfln("[landlock] permit path: {}", pathstr);
rtext::swallow_error("[landlock/path] omitting from sandbox: failed to add path", allow_path(ruleset_fd, pathstr));
errors::ext::swallow("[landlock/path] omitting from sandbox: failed to add path", allow_path(ruleset_fd, pathstr));
};
rtext::landlock_restrict_self(ruleset_fd)!;

View File

@@ -1,4 +1,5 @@
// vim: set shiftwidth=2 :
use errors::ext;
use fmt;
use fs;
use io;
@@ -60,8 +61,8 @@ export fn namespace_restrict(what: *resources) void = {
// (and the wrapper only exits once *all* orphaned children die).
// i don't need children to outlive the main process, so i fork once and let
// the sandboxed program be reaper for all its children.
rtext::check_error("[namespace/fork] forking new PID 1", fork_and_propagate());
// rtext::check_error("[namespace/fork] forking second time", fork_and_propagate());
errors::ext::check("[namespace/fork] forking new PID 1", fork_and_propagate());
// errors::ext::check("[namespace/fork] forking second time", fork_and_propagate());
};
let pwd = strings::dup(os::getcwd()); // dup because API uses a static buffer
@@ -69,7 +70,7 @@ export fn namespace_restrict(what: *resources) void = {
isolate_paths(what.paths);
// try to change to the old working directory;
// this can fail if it's not within the sandbox.
rtext::swallow_error("namespace: restore $PWD", os::chdir(pwd));
errors::ext::swallow("namespace: restore $PWD", os::chdir(pwd));
};
// fork and:
@@ -91,7 +92,7 @@ fn fork_and_propagate() (void | os::exec::error) = {
fn isolate_paths(paths: []path::buffer) void = {
// allow new mounts to propagate from the parent namespace into the child
// namespace, but not vice versa:
rtext::check_error("[namespace] reconfigure / as MS_SLAVE", rtext::mount("/", "/", "", rtext::mount_flag::SLAVE | rtext::mount_flag::REC, null));
errors::ext::check("[namespace] reconfigure / as MS_SLAVE", rtext::mount("/", "/", "", rtext::mount_flag::SLAVE | rtext::mount_flag::REC, null));
// in order to mount ANY directory from the old root into the new root,
// they have to be totally disparate. if we kept the old root at / and the new
@@ -102,16 +103,16 @@ fn isolate_paths(paths: []path::buffer) void = {
// 2. create a new rootfs at `new` and bind stuff into it.
// 3. then pivot a 2nd time, into `new` (and drop `old` altogether)
rtext::check_error("[namespace] mount -t tmpfs tmpfs /tmp", rtext::mount("tmpfs", "/tmp", "tmpfs", rtext::mount_flag::NODEV | rtext::mount_flag::NOSUID, null));
errors::ext::check("[namespace] mount -t tmpfs tmpfs /tmp", rtext::mount("tmpfs", "/tmp", "tmpfs", rtext::mount_flag::NODEV | rtext::mount_flag::NOSUID, null));
pivot_into("/tmp", "old");
// now we have `/`, empty except for the old rootfs available at `/old`
// prepare a new rootfs. it has to be its own mount (tmpfs), not just a dir.
rtext::check_error("[namespace] mkdir new", rt::mkdir("new", 0o755));
rtext::check_error("[namespace] mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", 0, null));
// rtext::check_error("[namespace] mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", rtext::mount_flag::NODEV | rtext::mount_flag::NOSUID, null));
// rtext::check_error("[namespace] mount -o rbind new new", rtext::mount("new", "new", "", rtext::mount_flag::BIND | rtext::mount_flag::REC, null));
errors::ext::check("[namespace] mkdir new", rt::mkdir("new", 0o755));
errors::ext::check("[namespace] mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", 0, null));
// errors::ext::check("[namespace] mount -t tmpfs tmpfs new", rtext::mount("tmpfs", "new", "tmpfs", rtext::mount_flag::NODEV | rtext::mount_flag::NOSUID, null));
// errors::ext::check("[namespace] mount -o rbind new new", rtext::mount("new", "new", "", rtext::mount_flag::BIND | rtext::mount_flag::REC, null));
// try to mount a new /proc.
// - this is "safe" because we're not doing anything
@@ -125,15 +126,15 @@ fn isolate_paths(paths: []path::buffer) void = {
// - bind-mounting /proc is _in theory_ safe (it's a namespace-aware fs),
// but in practice there are namespacing bugs at least as recently as 2021:
// <https://github.com/opencontainers/runc/issues/2826#issuecomment-915683044>
rtext::swallow_error("[namespace] mkdir new/proc", rt::mkdir("new/proc", 0o755));
rtext::swallow_error("[namespace] mount /new/proc", rtext::mount(
errors::ext::swallow("[namespace] mkdir new/proc", rt::mkdir("new/proc", 0o755));
errors::ext::swallow("[namespace] mount /new/proc", rtext::mount(
"proc", "new/proc", "proc", rtext::mount_flag::NOSUID | rtext::mount_flag::NOEXEC | rtext::mount_flag::NODEV, null
));
// provide a new `/tmp` too.
rtext::swallow_error("[namespace] mkdir new/tmp", rt::mkdir("new/tmp", 0o777));
rtext::swallow_error("[namespace] mount -t tmpfs tmpfs new/tmp", rtext::mount("tmpfs", "new/tmp", "tmpfs", 0, null));
errors::ext::swallow("[namespace] mkdir new/tmp", rt::mkdir("new/tmp", 0o777));
errors::ext::swallow("[namespace] mount -t tmpfs tmpfs new/tmp", rtext::mount("tmpfs", "new/tmp", "tmpfs", 0, null));
// bind all the user-requested paths from `old/$p` into `new/$p`.
// use the `dirfd` abstraction so that paths meant for `old` can't crawl out
@@ -231,7 +232,7 @@ fn bind_leaf(old_fs: *fs::fs, new_fs: *fs::fs, user_path: *path::buffer) void =
};
log::printfln("[namespace/bind] mount {} {}", path::string(&old_pathbuf), path::string(&new_pathbuf));
rtext::swallow_error("[namespace/bind] bind_leaf: mount", rtext::mount(
errors::ext::swallow("[namespace/bind] bind_leaf: mount", rtext::mount(
path::string(&old_pathbuf), path::string(&new_pathbuf), "", rtext::mount_flag::BIND | rtext::mount_flag::REC, null
));
};
@@ -281,17 +282,17 @@ fn bind_component(old_fs: *fs::fs, new_fs: *fs::fs, strpath: str, remaining: str
// at some directory (to be created) underneath it.
fn pivot_into(new_root: str, stash_old_root: (str|void) = void) void = {
log::printfln("[namespace] pivot_root {}", new_root);
rtext::check_error("[namespace] cd <new_root>", os::chdir(new_root));
errors::ext::check("[namespace] cd <new_root>", os::chdir(new_root));
match (stash_old_root) {
case let old: str =>
rtext::check_error("[namespace] mkdir <stash_old_root>", rt::mkdir(old, 0o755));
rtext::check_error("[namespace] pivot_root . <stash_old_root>", rtext::pivot_root(".", old));
errors::ext::check("[namespace] mkdir <stash_old_root>", rt::mkdir(old, 0o755));
errors::ext::check("[namespace] pivot_root . <stash_old_root>", rtext::pivot_root(".", old));
case void =>
rtext::check_error("[namespace] pivot_root . .", rtext::pivot_root(".", "."));
errors::ext::check("[namespace] pivot_root . .", rtext::pivot_root(".", "."));
// drop the old rootfs. weird idiom, but documented in `man 2 pivot_root`.
rtext::check_error("[namespace] umount .", rt::umount2(".", rtext::umount_flag::MNT_DETACH));
errors::ext::check("[namespace] umount .", rt::umount2(".", rtext::umount_flag::MNT_DETACH));
};
rtext::check_error("[namespace] cd /", os::chdir("/"));
errors::ext::check("[namespace] cd /", os::chdir("/"));
};
fn write_uid_map(uid: unix::uid, gid: unix::gid) void = {