syd 3.58.0

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/utils/syd-mdwe.rs: Run a command under Memory-Deny-Write-Execute protections
//
// Copyright (c) 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

// SAFETY: This utility has been liberated from unsafe code!
#![forbid(unsafe_code)]

use std::process::ExitCode;

use nix::errno::Errno;
use syd::{
    confine::{confine_mdwe, confine_scmp_wx_all, run_syd_shell},
    eprintfln,
    err::err2exit,
    printfln,
};

// Set global allocator to GrapheneOS allocator.
#[cfg(all(
    not(target_os = "android"),
    not(target_arch = "loongarch64"),
    not(target_arch = "riscv64"),
    target_page_size_4k,
    target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;

syd::main! {
    use lexopt::prelude::*;

    syd::set_sigpipe_dfl()?;

    // Parse CLI options.
    //
    // Note, option parsing is POSIXly correct:
    // POSIX recommends that no more options are parsed after the first
    // positional argument. The other arguments are then all treated as
    // positional arguments.
    // See: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02
    let mut opt_mdwe = false;
    let mut opt_scmp = false;
    let mut opt_cmd = None;
    let mut opt_arg = Vec::new();

    let mut parser = lexopt::Parser::from_env();
    while let Some(arg) = parser.next()? {
        match arg {
            Short('h') => {
                help()?;
                return Ok(ExitCode::SUCCESS);
            }
            Short('m') => opt_mdwe = true,
            Short('s') => opt_scmp = true,
            Value(prog) => {
                opt_cmd = Some(prog);
                opt_arg.extend(parser.raw_args()?);
            }
            _ => return Err(arg.unexpected().into()),
        }
    }

    // PR_SET_MDWE is fatal only if -m is given.
    let opt_mdwe_fatal = opt_mdwe;
    if !opt_mdwe && !opt_scmp {
        // Default is to enable both.
        opt_mdwe = true;
        opt_scmp = true;
    }

    if opt_mdwe {
        if let Err(errno) = confine_mdwe(false) {
            eprintfln!("prctl failed to set Memory-Deny-Write-Execute: {errno}!")?;
            if opt_mdwe_fatal {
                return Err(errno.into());
            }
        }
    }

    if opt_scmp {
        if let Err(error) = confine_scmp_wx_all() {
            let _ = eprintfln!("seccomp failed to set W^X restrictions: {error}!");
            return Err(error);
        }
    }

    // Execute command, Syd shell by default.
    Ok(err2exit(run_syd_shell(opt_cmd, opt_arg)))
}

fn help() -> Result<(), Errno> {
    printfln!("Usage: syd-mdwe [-hms] {{command [args..]}}")?;
    printfln!("Run a command under Memory-Deny-Write-Execute protections.")?;
    printfln!("Use -m to enable protections using prctl(2) PR_SET_MDWE (default).")?;
    printfln!("Use -s to enable protections using seccomp(2) (use with -m to enable both).")?;
    Ok(())
}