From 9de68eaaa04c8a02ac5d7d6c3c854e591dc8b897 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 17 Mar 2024 11:26:20 +0100 Subject: [PATCH 01/25] cargo fmt, fix some warnings --- examples/ipc_server_with_database.rs | 110 ++++++------- examples/network_server.rs | 14 +- examples/no_files_allow_stdout.rs | 2 +- examples/server_with_database.rs | 101 +++++++----- examples/simple_network.rs | 45 +++--- examples/time.rs | 20 +-- examples/user-guide.rs | 40 ++--- src/builtins/basic.rs | 16 +- src/builtins/danger_zone.rs | 14 +- src/builtins/mod.rs | 4 +- src/builtins/network.rs | 81 ++++++---- src/builtins/pipes.rs | 3 +- src/builtins/systemio.rs | 129 ++++++++++------ src/builtins/time.rs | 12 +- src/error.rs | 4 +- src/landlock.rs | 10 +- src/lib.rs | 90 +++++++---- src/macros.rs | 76 +++++++-- tests/arg_comparisons.rs | 44 +++--- tests/bad_combination.rs | 77 ++++----- tests/default_deny.rs | 9 +- tests/inherit_filters.rs | 5 +- tests/landlock_allthreads_fail.rs | 16 +- tests/landlock_basic.rs | 223 ++++++++++++++++++--------- tests/landlock_conflicts.rs | 86 ++++++----- tests/multiple_conditions.rs | 25 +-- tests/multiple_filters.rs | 37 ++--- tests/network.rs | 88 ++++++----- tests/ruleset_union.rs | 63 ++++---- tests/sleep.rs | 22 +-- tests/test_ref_ruleset.rs | 6 +- tests/tests_can_fail.rs | 3 +- tests/thread_multi.rs | 8 +- tests/thread_single.rs | 9 +- tests/unsupported_os.rs | 3 +- 35 files changed, 873 insertions(+), 622 deletions(-) diff --git a/examples/ipc_server_with_database.rs b/examples/ipc_server_with_database.rs index 1292f2f..f3c1a77 100644 --- a/examples/ipc_server_with_database.rs +++ b/examples/ipc_server_with_database.rs @@ -36,7 +36,10 @@ type DbConn = Arc>; fn run_subprocess(cmd: &[&str]) -> std::process::Child { let exe_path = std::env::current_exe().unwrap(); - let args: Vec<_> = ["run_main", "--", "--sub"].iter().chain(cmd.iter()).collect(); + let args: Vec<_> = ["run_main", "--", "--sub"] + .iter() + .chain(cmd.iter()) + .collect(); std::process::Command::new(exe_path.to_str().unwrap()) .arg0(format!("{}-subprocess", cmd[0])) @@ -63,13 +66,14 @@ fn run_webserver(db_socket_path: &str) { // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build().unwrap(); + .build() + .unwrap(); let listener = std::net::TcpListener::bind("127.0.0.1:5576").unwrap(); // extrasafe context SafetyContext::new() - .enable(Networking::nothing() - .allow_running_tcp_servers()).unwrap() + .enable(Networking::nothing().allow_running_tcp_servers()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -111,8 +115,7 @@ fn run_webserver(db_socket_path: &str) { .to_string(); messages - }) - ); + })); let svc = warp::service(routes); let make_svc = hyper::service::make_service_fn(move |_| { @@ -147,25 +150,31 @@ fn run_db(socket_path: &str) { db.pragma_update(None, "locking_mode", "exclusive").unwrap(); db.pragma_update(None, "journal_mode", "wal").unwrap(); - db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []).unwrap(); + db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []) + .unwrap(); let mut get_rows = db.prepare("SELECT msg FROM messages;").unwrap(); let mut insert_row = db.prepare("INSERT INTO messages VALUES (?)").unwrap(); // after opening connection socket and db file, set extrasafe context SafetyContext::new() - .enable(Networking::nothing() - .allow_connect() - .yes_really() - .allow_running_unix_servers() - ).unwrap() - .enable(SystemIO::nothing() - .allow_read() - .allow_write() - .allow_metadata() - .allow_ioctl() - .allow_close()).unwrap() - .enable(Threads::nothing() - .allow_sleep().yes_really()).unwrap() + .enable( + Networking::nothing() + .allow_connect() + .yes_really() + .allow_running_unix_servers(), + ) + .unwrap() + .enable( + SystemIO::nothing() + .allow_read() + .allow_write() + .allow_metadata() + .allow_ioctl() + .allow_close(), + ) + .unwrap() + .enable(Threads::nothing().allow_sleep().yes_really()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -197,18 +206,17 @@ fn run_db(socket_path: &str) { let msg: DBMsg; if buf == "list" { msg = DBMsg::List; - } - else if buf.starts_with("write") { + } else if buf.starts_with("write") { msg = DBMsg::Write(buf[6..].to_string()); - } - else { + } else { panic!("unknown message recieved in db: {}", buf); } match msg { DBMsg::List => { let messages: Vec = get_rows - .query_map([], |row| row.get(0)).unwrap() + .query_map([], |row| row.get(0)) + .unwrap() .map(Result::unwrap) .collect(); @@ -231,8 +239,8 @@ fn run_client_write(msg: &str) { // Set up extrasafe context SafetyContext::new() - .enable(Networking::nothing() - .allow_start_tcp_clients()).unwrap() + .enable(Networking::nothing().allow_start_tcp_clients()) + .unwrap() .apply_to_current_thread() .unwrap(); println!("about to make request with msg {}", msg); @@ -271,34 +279,35 @@ fn run_client_read() { // enable extrasafe context let ctx = SafetyContext::new() - .enable(Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers().yes_really() - .allow_start_tcp_clients() - ).unwrap() + .enable( + Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers() + .yes_really() + .allow_start_tcp_clients(), + ) + .unwrap() // For some reason only if we make two requests with a client does it use multiple threads, // so we only need them in the reader thread rather than the writer. - .enable(Threads::nothing() - .allow_create()).unwrap(); + .enable(Threads::nothing().allow_create()) + .unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx.enable( + let ctx = ctx + .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ).unwrap(); + ) + .unwrap(); #[cfg(feature = "landlock")] - let ctx = ctx.enable( - SystemIO::nothing() - .allow_dns_files() - .allow_ssl_files() - ).unwrap(); - - ctx.apply_to_current_thread() + let ctx = ctx + .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) .unwrap(); + ctx.apply_to_current_thread().unwrap(); // make request runtime.block_on(async { @@ -333,16 +342,13 @@ fn main() { if args.contains(&"--sub".into()) { // If args is "example_prog [possible other options] --sub subcommand subargs...", run the subcommand if let Some(idx) = args.iter().position(|s| s == "db") { - run_db(&args[idx+1]); - } - else if let Some(idx) = args.iter().position(|s| s == "webserver") { - run_webserver(&args[idx+1]); - } - else if args.contains(&"read_client".into()) { + run_db(&args[idx + 1]); + } else if let Some(idx) = args.iter().position(|s| s == "webserver") { + run_webserver(&args[idx + 1]); + } else if args.contains(&"read_client".into()) { run_client_read(); - } - else if let Some(idx) = args.iter().position(|s| s == "write_client") { - run_client_write(&args[idx+1]); + } else if let Some(idx) = args.iter().position(|s| s == "write_client") { + run_client_write(&args[idx + 1]); } return; } diff --git a/examples/network_server.rs b/examples/network_server.rs index cba3b6d..8533f02 100644 --- a/examples/network_server.rs +++ b/examples/network_server.rs @@ -87,12 +87,14 @@ fn main() { // we can enable safetycontext, rather than just waiting 50ms. thread::sleep(std::time::Duration::from_millis(50)); SafetyContext::new() - .enable(Networking::nothing() - .allow_running_tcp_servers() - .allow_start_tcp_clients() - ).unwrap() - .enable(Threads::nothing() - .allow_create()).unwrap() + .enable( + Networking::nothing() + .allow_running_tcp_servers() + .allow_start_tcp_clients(), + ) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_all_threads() .unwrap(); diff --git a/examples/no_files_allow_stdout.rs b/examples/no_files_allow_stdout.rs index e85d4e9..d8ee6e7 100644 --- a/examples/no_files_allow_stdout.rs +++ b/examples/no_files_allow_stdout.rs @@ -11,7 +11,7 @@ fn main() { .enable( extrasafe::builtins::SystemIO::nothing() .allow_stdout() - .allow_stderr() + .allow_stderr(), ) .unwrap() .apply_to_all_threads(); diff --git a/examples/server_with_database.rs b/examples/server_with_database.rs index 77d4c89..5f9fcba 100644 --- a/examples/server_with_database.rs +++ b/examples/server_with_database.rs @@ -61,18 +61,20 @@ fn run_server() { // spawn db server thread std::thread::Builder::new() .name("db".into()) - .spawn(move || run_db(&db_queue)).unwrap(); - + .spawn(move || run_db(&db_queue)) + .unwrap(); + // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build().unwrap(); + .build() + .unwrap(); let listener = std::net::TcpListener::bind("127.0.0.1:5575").unwrap(); // extrasafe context SafetyContext::new() - .enable(Networking::nothing() - .allow_running_tcp_servers()).unwrap() + .enable(Networking::nothing().allow_running_tcp_servers()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -98,8 +100,7 @@ fn run_server() { let messages = recv.recv().unwrap(); messages.join("\n") - }) - ); + })); let svc = warp::service(routes); let make_svc = hyper::service::make_service_fn(move |_| { @@ -130,20 +131,24 @@ fn run_db(queue: &DbConn) { db.pragma_update(None, "locking_mode", "exclusive").unwrap(); db.pragma_update(None, "journal_mode", "wal").unwrap(); - db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []).unwrap(); + db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []) + .unwrap(); let mut get_rows = db.prepare("SELECT msg FROM messages;").unwrap(); let mut insert_row = db.prepare("INSERT INTO messages VALUES (?)").unwrap(); // after opening file, set extrasafe context SafetyContext::new() - .enable(SystemIO::nothing() - .allow_read() - .allow_write() - .allow_metadata() - .allow_ioctl() - .allow_close()).unwrap() - .enable(Threads::nothing() - .allow_sleep().yes_really()).unwrap() + .enable( + SystemIO::nothing() + .allow_read() + .allow_write() + .allow_metadata() + .allow_ioctl() + .allow_close(), + ) + .unwrap() + .enable(Threads::nothing().allow_sleep().yes_really()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -162,7 +167,8 @@ fn run_db(queue: &DbConn) { match msg { DBMsg::List(send) => { let messages: Vec = get_rows - .query_map([], |row| row.get(0)).unwrap() + .query_map([], |row| row.get(0)) + .unwrap() .map(Result::unwrap) .collect(); @@ -179,12 +185,13 @@ fn run_client_write(msg: &str) { // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build().unwrap(); + .build() + .unwrap(); // Set up extrasafe context SafetyContext::new() - .enable(Networking::nothing() - .allow_start_tcp_clients()).unwrap() + .enable(Networking::nothing().allow_start_tcp_clients()) + .unwrap() .apply_to_current_thread() .unwrap(); println!("about to make request with msg {}", msg); @@ -216,40 +223,43 @@ fn run_client_read() { let runtime = tokio::runtime::Builder::new_current_thread() .worker_threads(1) .enable_all() - .build().unwrap(); + .build() + .unwrap(); let client = reqwest::Client::new(); // enable extrasafe context let ctx = SafetyContext::new() - .enable(Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers().yes_really() - .allow_start_tcp_clients() - ).unwrap() + .enable( + Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers() + .yes_really() + .allow_start_tcp_clients(), + ) + .unwrap() // For some reason only if we make two requests with a client does it use multiple threads, // so we only need them in the reader thread rather than the writer. - .enable(Threads::nothing() - .allow_create()).unwrap(); + .enable(Threads::nothing().allow_create()) + .unwrap(); #[cfg(feature = "landlock")] - let ctx = ctx.enable( - SystemIO::nothing() - .allow_dns_files() - .allow_ssl_files() - ).unwrap(); + let ctx = ctx + .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) + .unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx.enable( + let ctx = ctx + .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ).unwrap(); - - ctx.apply_to_current_thread() + ) .unwrap(); + ctx.apply_to_current_thread().unwrap(); + // make request runtime.block_on(async { // Show that we can resolve dns and do ssl. Data returned isn't checked or used anywhere, @@ -262,7 +272,10 @@ fn run_client_read() { res.unwrap_err() ); let text = res.unwrap(); - println!("first 10 bytes of response from example.org {}", &text[..10]); + println!( + "first 10 bytes of response from example.org {}", + &text[..10] + ); let res = client.get("http://127.0.0.1:5575/read").send().await; assert!( @@ -281,7 +294,8 @@ fn main() { // -- Spawn server let _server_thread = std::thread::Builder::new() .name("server".into()) - .spawn(run_server).unwrap(); + .spawn(run_server) + .unwrap(); // give server time to start up std::thread::sleep(std::time::Duration::from_millis(100)); @@ -289,7 +303,8 @@ fn main() { // -- write "hello" to db let client1_thread = std::thread::Builder::new() .name("client1".into()) - .spawn(|| run_client_write("hello")).unwrap(); + .spawn(|| run_client_write("hello")) + .unwrap(); let res1 = client1_thread.join(); assert!(res1.is_ok(), "client1 failed: {:?}", res1.unwrap_err()); @@ -297,7 +312,8 @@ fn main() { // -- write "extrasafe" to db let client2_thread = std::thread::Builder::new() .name("client2".into()) - .spawn(|| run_client_write("extrasafe")).unwrap(); + .spawn(|| run_client_write("extrasafe")) + .unwrap(); let res2 = client2_thread.join(); assert!(res2.is_ok(), "client2 failed: {:?}", res2.unwrap_err()); @@ -305,7 +321,8 @@ fn main() { // -- read back, check messages are there in order let client3_thread = std::thread::Builder::new() .name("client3".into()) - .spawn(run_client_read).unwrap(); + .spawn(run_client_read) + .unwrap(); let res3 = client3_thread.join(); assert!(res3.is_ok(), "client3 failed: {:?}", res3.unwrap_err()); } diff --git a/examples/simple_network.rs b/examples/simple_network.rs index 7d5d7ae..a6eedfa 100644 --- a/examples/simple_network.rs +++ b/examples/simple_network.rs @@ -5,52 +5,54 @@ //! the root certificate store is included in the binary. See the `Cargo.toml` configuration for //! musl. -use extrasafe::*; use extrasafe::builtins::{danger_zone::Threads, Networking, SystemIO}; +use extrasafe::*; fn main() { // do as much setup before enabling extrasafe so we can enable the least amount of syscalls let runtime = tokio::runtime::Builder::new_current_thread() .worker_threads(1) .enable_all() - .build().unwrap(); + .build() + .unwrap(); let client = reqwest::Client::new(); let ctx = SafetyContext::new() - .enable(Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers().yes_really() - .allow_start_tcp_clients() - ).unwrap() + .enable( + Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers() + .yes_really() + .allow_start_tcp_clients(), + ) + .unwrap() // hyper (via reqwest) seems to want to spawn a separate blocking thread to do DNS and it // doesn't seem like it can be preallocated easily. // TODO: investigate using runtime::Builder::thread_keep_alive and max_blocking_threads to // effectively preallocate by then just doing `block_on(|| ())` - .enable(Threads::nothing() - .allow_create() - ).unwrap(); + .enable(Threads::nothing().allow_create()) + .unwrap(); // allow access to dns and ssl files // note that allowing ssl file access isn't necessary if using rustls with webpki-certs // and allowing the dns files isn't strictly necessary either depending on various system // configurations #[cfg(feature = "landlock")] - let ctx = ctx.enable( - SystemIO::nothing() - .allow_dns_files() - .allow_ssl_files() - ).unwrap(); + let ctx = ctx + .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) + .unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx.enable( + let ctx = ctx + .enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ).unwrap(); - - ctx.apply_to_current_thread() + ) .unwrap(); + ctx.apply_to_current_thread().unwrap(); + // make an http request runtime.block_on(async { // Show that we can resolve dns and do ssl. Data returned isn't checked or used anywhere, @@ -63,7 +65,10 @@ fn main() { res.unwrap_err() ); let text = res.unwrap(); - println!("first 10 bytes of response from example.org {}", &text[..10]); + println!( + "first 10 bytes of response from example.org {}", + &text[..10] + ); }); } diff --git a/examples/time.rs b/examples/time.rs index 7abf07b..256792d 100644 --- a/examples/time.rs +++ b/examples/time.rs @@ -1,20 +1,16 @@ -use extrasafe::{SafetyContext, builtins}; -use builtins::{Time, SystemIO}; +use builtins::{SystemIO, Time}; +use extrasafe::{builtins, SafetyContext}; fn main() { SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_stdout() - ).unwrap() - + .enable(SystemIO::nothing().allow_stdout()) + .unwrap() // On most systems this won't have an effect because glibc and musl both use vDSOs that // compute time directly via rdtsc rather than calling the syscalls directly. - .enable( - Time::nothing() - .allow_gettime() - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(Time::nothing().allow_gettime()) + .unwrap() + .apply_to_current_thread() + .unwrap(); let time = std::time::SystemTime::now(); println!("time gave us: {:#?}", time); diff --git a/examples/user-guide.rs b/examples/user-guide.rs index 2937d88..0ff4f9e 100644 --- a/examples/user-guide.rs +++ b/examples/user-guide.rs @@ -1,20 +1,19 @@ fn simple_example() { - use extrasafe::builtins::{SystemIO, Networking}; + use extrasafe::builtins::{Networking, SystemIO}; let ctx = extrasafe::SafetyContext::new(); let ctx = ctx - .enable( - SystemIO::nothing() - .allow_open_readonly() - ).expect("Failed to add systemio ruleset to context") + .enable(SystemIO::nothing().allow_open_readonly()) + .expect("Failed to add systemio ruleset to context") // The Networking RuleSet includes both read and write, but our files will be opened // readonly so we can't actually write to them. We can still write to stdout and stderr // though. .enable( Networking::nothing() .allow_start_tcp_clients() - .allow_running_tcp_clients() - ).expect("Failed to add networking ruleset to context"); + .allow_running_tcp_clients(), + ) + .expect("Failed to add networking ruleset to context"); ctx.apply_to_current_thread() .expect("Failed to apply seccomp filters"); @@ -22,8 +21,8 @@ fn simple_example() { } fn custom_ruleset() { - use extrasafe::*; use extrasafe::syscalls::Sysno; + use extrasafe::*; use std::collections::HashMap; @@ -40,11 +39,8 @@ fn custom_ruleset() { const SOCK_STREAM: u64 = libc::SOCK_STREAM as u64; let rule = SeccompRule::new(Sysno::socket) - .and_condition( - seccomp_arg_filter!(arg0 & SOCK_STREAM == SOCK_STREAM)); - HashMap::from([ - (Sysno::socket, vec![rule,]) - ]) + .and_condition(seccomp_arg_filter!(arg0 & SOCK_STREAM == SOCK_STREAM)); + HashMap::from([(Sysno::socket, vec![rule])]) } fn name(&self) -> &'static str { @@ -53,8 +49,10 @@ fn custom_ruleset() { } extrasafe::SafetyContext::new() - .enable(MyRuleSet).unwrap() - .apply_to_current_thread().unwrap(); + .enable(MyRuleSet) + .unwrap() + .apply_to_current_thread() + .unwrap(); } #[cfg(feature = "landlock")] @@ -65,11 +63,13 @@ fn with_landlock() { extrasafe::SafetyContext::new() .enable( - extrasafe::builtins::SystemIO::nothing() - .allow_create_in_dir(&tmp_dir_allow) - .allow_write_file(&tmp_dir_allow) - ).unwrap() - .apply_to_current_thread().unwrap(); + extrasafe::builtins::SystemIO::nothing() + .allow_create_in_dir(&tmp_dir_allow) + .allow_write_file(&tmp_dir_allow), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); // Opening arbitrary files now fails! let res = File::create(tmp_dir_deny.join("evil.txt")); diff --git a/src/builtins/basic.rs b/src/builtins/basic.rs index dd9feb1..6c6d0ca 100644 --- a/src/builtins/basic.rs +++ b/src/builtins/basic.rs @@ -1,11 +1,9 @@ //! Contains a [`RuleSet`] for allowing base syscalls that all programs will need, and are not //! dangerous for the most part. -use std::collections::HashMap; - use syscalls::Sysno; -use crate::{SeccompRule, RuleSet}; +use crate::RuleSet; /// A [`RuleSet`] allowing basic required syscalls to do things like allocate memory, and also a few that are used by /// Rust to set up panic handling and segfault handlers. @@ -26,7 +24,6 @@ impl RuleSet for BasicCapabilities { Sysno::mprotect, Sysno::munlock, Sysno::munlockall, - // Rust installs a signal handler to distinguish stack overflows from other faults // https://github.com/iximeow/rust/blob/master/src/libstd/sys/unix/stack_overflow.rs#L46 // (I learned this by getting a segfault when not allowing sigaction/etc and then @@ -36,35 +33,28 @@ impl RuleSet for BasicCapabilities { Sysno::rt_sigaction, Sysno::rt_sigprocmask, Sysno::rt_sigreturn, - // Futex management Sysno::futex, Sysno::get_robust_list, Sysno::set_robust_list, - // Readlink isn't dangerous because you still need to be able to open the file to do // anything with the resolved name. Sysno::readlink, - // Getpid/tid is fine. Sysno::getpid, Sysno::gettid, - // Get kernel info Sysno::uname, - // Could maybe put in a separate ruleset Sysno::getrandom, - // Thread affinity and yield seems okay to put here but I could be convinced to put it // in the Multiprocessing ruleset. they probably should be there. - Sysno::sched_getaffinity, Sysno::sched_setaffinity, + Sysno::sched_getaffinity, + Sysno::sched_setaffinity, Sysno::sched_yield, - // rseq is used in newer glibc for some initialization purposes. // It's kind of complicated but does not appear to be dangerous. Sysno::rseq, - // Exiting is probably fine. Sysno::exit, Sysno::exit_group, diff --git a/src/builtins/danger_zone.rs b/src/builtins/danger_zone.rs index 8e18bca..44d53fe 100644 --- a/src/builtins/danger_zone.rs +++ b/src/builtins/danger_zone.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use syscalls::Sysno; -use crate::{SeccompRule, RuleSet}; +use crate::{RuleSet, SeccompRule}; use super::YesReally; @@ -91,10 +91,14 @@ pub struct ForkAndExec; impl RuleSet for ForkAndExec { fn simple_rules(&self) -> Vec { let mut rules = vec![ - Sysno::fork, Sysno::vfork, - Sysno::execve, Sysno::execveat, - Sysno::wait4, Sysno::waitid, - Sysno::clone, Sysno::clone3, + Sysno::fork, + Sysno::vfork, + Sysno::execve, + Sysno::execveat, + Sysno::wait4, + Sysno::waitid, + Sysno::clone, + Sysno::clone3, ]; // musl creates a pipe when it starts a new process, and fails the operation if it can't diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 43b3c50..c24ecf7 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -16,9 +16,7 @@ impl YesReally { /// Make a [`YesReally`]. pub fn new(inner: T) -> YesReally { - YesReally { - inner, - } + YesReally { inner } } } diff --git a/src/builtins/network.rs b/src/builtins/network.rs index c959c1d..5c6bb09 100644 --- a/src/builtins/network.rs +++ b/src/builtins/network.rs @@ -5,39 +5,58 @@ use std::collections::{HashMap, HashSet}; use syscalls::Sysno; use super::YesReally; -use crate::{SeccompRule, RuleSet}; +use crate::{RuleSet, SeccompRule}; // TODO: make bind calls conditional on the DGRAM/UNIX/STREAM flag in each function // TODO: add io_uring const NET_IO_SYSCALLS: &[Sysno] = &[ - Sysno::epoll_create, Sysno::epoll_create1, - Sysno::epoll_ctl, Sysno::epoll_wait, Sysno::epoll_pwait, Sysno::epoll_pwait2, - Sysno::select, Sysno::pselect6, - Sysno::poll, Sysno::ppoll, - - Sysno::accept, Sysno::accept4, - + Sysno::epoll_create, + Sysno::epoll_create1, + Sysno::epoll_ctl, + Sysno::epoll_wait, + Sysno::epoll_pwait, + Sysno::epoll_pwait2, + Sysno::select, + Sysno::pselect6, + Sysno::poll, + Sysno::ppoll, + Sysno::accept, + Sysno::accept4, // used in reqwest::blocking I guess to notify when blocking reads finish? - Sysno::eventfd, Sysno::eventfd2, - + Sysno::eventfd, + Sysno::eventfd2, // Used to set tcp_nodelay - Sysno::fcntl, Sysno::ioctl, + Sysno::fcntl, + Sysno::ioctl, Sysno::getsockopt, Sysno::setsockopt, - // Misc socket info Sysno::getpeername, Sysno::getsockname, ]; // listen is technically not a "read" syscall but you'd never listen and not read. -const NET_READ_SYSCALLS: &[Sysno] = &[Sysno::listen, - Sysno::recvfrom, Sysno::recvmsg, Sysno::recvmmsg, - Sysno::read, Sysno::readv, Sysno::preadv, Sysno::preadv2]; -const NET_WRITE_SYSCALLS: &[Sysno] = &[Sysno::sendto, Sysno::sendmsg, Sysno::sendmmsg, - Sysno::sendfile, - Sysno::write, Sysno::writev, Sysno::pwritev, Sysno::pwritev2]; +const NET_READ_SYSCALLS: &[Sysno] = &[ + Sysno::listen, + Sysno::recvfrom, + Sysno::recvmsg, + Sysno::recvmmsg, + Sysno::read, + Sysno::readv, + Sysno::preadv, + Sysno::preadv2, +]; +const NET_WRITE_SYSCALLS: &[Sysno] = &[ + Sysno::sendto, + Sysno::sendmsg, + Sysno::sendmmsg, + Sysno::sendfile, + Sysno::write, + Sysno::writev, + Sysno::pwritev, + Sysno::pwritev2, +]; // TODO: refactor Socket rule creation to reduce duplication in the allow_start_*_server functions @@ -105,14 +124,16 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); @@ -151,14 +172,16 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); @@ -195,17 +218,19 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); - + self.allowed.extend(&[Sysno::connect]); self.allowed.extend(NET_IO_SYSCALLS); self.allowed.extend(NET_READ_SYSCALLS); @@ -242,14 +267,16 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_UNIX == AF_UNIX)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // DGRAM let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_UNIX == AF_UNIX)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom.entry(Sysno::socket) + self.custom + .entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); diff --git a/src/builtins/pipes.rs b/src/builtins/pipes.rs index f5bfe13..679bb59 100644 --- a/src/builtins/pipes.rs +++ b/src/builtins/pipes.rs @@ -1,8 +1,7 @@ //! Contains a [`RuleSet`] for allowing pipes -use std::collections::HashMap; +use crate::RuleSet; use syscalls::Sysno; -use crate::{SeccompRule, RuleSet}; /// [`Pipes`] allows you to create anonymous pipes for inter-process communication via the `pipe` /// syscalls. diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index 9ff3dfe..79354aa 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -1,6 +1,6 @@ //! Contains a [`RuleSet`] for allowing IO-related syscalls, like file opening, reading, and writing. -use std::collections::{HashSet, HashMap}; +use std::collections::{HashMap, HashSet}; use std::fs::File; use std::os::unix::io::AsRawFd; @@ -9,24 +9,45 @@ use std::path::{Path, PathBuf}; use syscalls::Sysno; -#[cfg(feature = "landlock")] -use crate::LandlockRule; #[cfg(feature = "landlock")] use crate::landlock::{access, AccessFs, BitFlags}; +#[cfg(feature = "landlock")] +use crate::LandlockRule; -use crate::{RuleSet, SeccompRule}; use super::YesReally; +use crate::{RuleSet, SeccompRule}; -pub(crate) const IO_READ_SYSCALLS: &[Sysno] = &[Sysno::read, Sysno::readv, Sysno::preadv, Sysno::preadv2, Sysno::pread64, Sysno::lseek]; -pub(crate) const IO_WRITE_SYSCALLS: &[Sysno] = &[Sysno::write, Sysno::writev, Sysno::pwritev, Sysno::pwritev2, Sysno::pwrite64, - Sysno::fsync, Sysno::fdatasync, Sysno::lseek]; +pub(crate) const IO_READ_SYSCALLS: &[Sysno] = &[ + Sysno::read, + Sysno::readv, + Sysno::preadv, + Sysno::preadv2, + Sysno::pread64, + Sysno::lseek, +]; +pub(crate) const IO_WRITE_SYSCALLS: &[Sysno] = &[ + Sysno::write, + Sysno::writev, + Sysno::pwritev, + Sysno::pwritev2, + Sysno::pwrite64, + Sysno::fsync, + Sysno::fdatasync, + Sysno::lseek, +]; pub(crate) const IO_OPEN_SYSCALLS: &[Sysno] = &[Sysno::open, Sysno::openat, Sysno::openat2]; pub(crate) const IO_IOCTL_SYSCALLS: &[Sysno] = &[Sysno::ioctl, Sysno::fcntl]; // TODO: may want to separate fd-based and filename-based? -pub(crate) const IO_METADATA_SYSCALLS: &[Sysno] = &[Sysno::stat, Sysno::fstat, Sysno::newfstatat, - Sysno::lstat, Sysno::statx, - Sysno::getdents, Sysno::getdents64, - Sysno::getcwd]; +pub(crate) const IO_METADATA_SYSCALLS: &[Sysno] = &[ + Sysno::stat, + Sysno::fstat, + Sysno::newfstatat, + Sysno::lstat, + Sysno::statx, + Sysno::getdents, + Sysno::getdents64, + Sysno::getcwd, +]; pub(crate) const IO_CLOSE_SYSCALLS: &[Sysno] = &[Sysno::close, Sysno::close_range]; pub(crate) const IO_UNLINK_SYSCALLS: &[Sysno] = &[Sysno::unlink, Sysno::unlinkat]; @@ -56,7 +77,7 @@ impl SystemIO { allowed: HashSet::new(), custom: HashMap::new(), #[cfg(feature = "landlock")] - landlock_rules: HashMap::new() + landlock_rules: HashMap::new(), } } @@ -65,7 +86,8 @@ impl SystemIO { SystemIO::nothing() .allow_read() .allow_write() - .allow_open().yes_really() + .allow_open() + .yes_really() .allow_metadata() .allow_unlink() .allow_close() @@ -122,18 +144,20 @@ impl SystemIO { // WRONLY or RDWR is required for O_TMPFILE so we're fine to leave it out anyway. // const O_TMPFILE: u64 = libc::O_TMPFILE as u64; - const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL;// | O_TMPFILE; + const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL; // | O_TMPFILE; // flags are the second argument for open but the third for openat let rule = SeccompRule::new(Sysno::open) .and_condition(seccomp_arg_filter!(arg1 & WRITECREATE == 0)); - self.custom.entry(Sysno::open) + self.custom + .entry(Sysno::open) .or_insert_with(Vec::new) .push(rule); let rule = SeccompRule::new(Sysno::openat) .and_condition(seccomp_arg_filter!(arg2 & WRITECREATE == 0)); - self.custom.entry(Sysno::openat) + self.custom + .entry(Sysno::openat) .or_insert_with(Vec::new) .push(rule); @@ -163,9 +187,9 @@ impl SystemIO { /// Allow reading from stdin pub fn allow_stdin(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::read) - .and_condition(seccomp_arg_filter!(arg0 == 0)); - self.custom.entry(Sysno::read) + let rule = SeccompRule::new(Sysno::read).and_condition(seccomp_arg_filter!(arg0 == 0)); + self.custom + .entry(Sysno::read) .or_insert_with(Vec::new) .push(rule); @@ -174,9 +198,9 @@ impl SystemIO { /// Allow writing to stdout pub fn allow_stdout(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::write) - .and_condition(seccomp_arg_filter!(arg0 == 1)); - self.custom.entry(Sysno::write) + let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == 1)); + self.custom + .entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -185,9 +209,9 @@ impl SystemIO { /// Allow writing to stderr pub fn allow_stderr(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::write) - .and_condition(seccomp_arg_filter!(arg0 == 2)); - self.custom.entry(Sysno::write) + let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == 2)); + self.custom + .entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -203,18 +227,21 @@ impl SystemIO { /// it's possible that the fd will be reused and therefore may be read from. #[allow(clippy::missing_panics_doc)] pub fn allow_file_read(mut self, file: &File) -> SystemIO { - let fd = file.as_raw_fd().try_into().expect("provided fd was negative"); + let fd = file + .as_raw_fd() + .try_into() + .expect("provided fd was negative"); for &syscall in IO_READ_SYSCALLS { - let rule = SeccompRule::new(syscall) - .and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom.entry(syscall) + let rule = SeccompRule::new(syscall).and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom + .entry(syscall) .or_insert_with(Vec::new) .push(rule); } for &syscall in IO_METADATA_SYSCALLS { - let rule = SeccompRule::new(syscall) - .and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom.entry(syscall) + let rule = SeccompRule::new(syscall).and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom + .entry(syscall) .or_insert_with(Vec::new) .push(rule); } @@ -231,10 +258,13 @@ impl SystemIO { /// it's possible that the fd will be reused and therefore may be written to. #[allow(clippy::missing_panics_doc)] pub fn allow_file_write(mut self, file: &File) -> SystemIO { - let fd = file.as_raw_fd().try_into().expect("provided fd was negative"); - let rule = SeccompRule::new(Sysno::write) - .and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom.entry(Sysno::write) + let fd = file + .as_raw_fd() + .try_into() + .expect("provided fd was negative"); + let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom + .entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -267,7 +297,9 @@ impl RuleSet for SystemIO { impl SystemIO { fn insert_flags>(&mut self, path: P, new_flags: BitFlags) { let path = path.as_ref().to_path_buf(); - let _flag = self.landlock_rules.entry(path.clone()) + let _flag = self + .landlock_rules + .entry(path.clone()) .and_modify(|existing_flags| existing_flags.access_rules.insert(new_flags)) .or_insert_with(|| LandlockRule::new(&path, new_flags)); } @@ -286,7 +318,8 @@ impl SystemIO { self.allow_close() .allow_read() .allow_metadata() - .allow_open().yes_really() + .allow_open() + .yes_really() } /// Use Landlock to allow only the specified file to be written to. If this function is called @@ -302,7 +335,8 @@ impl SystemIO { self.allow_close() .allow_write() .allow_metadata() - .allow_open().yes_really() + .allow_open() + .yes_really() } /// Use Landlock to allow files to be created in the given directory. If this function is called @@ -331,7 +365,8 @@ impl SystemIO { self.allow_metadata() .allow_close() .allow_ioctl() - .allow_open().yes_really() + .allow_open() + .yes_really() } /// Use Landlock to allow creating directories. If this function is called multiple times, all @@ -400,20 +435,28 @@ impl SystemIO { self.allow_close() .allow_read() .allow_metadata() - .allow_open().yes_really() + .allow_open() + .yes_really() } /// Use Landlock to allow access to DNS files, like /etc/resolv.conf pub fn allow_dns_files(mut self) -> SystemIO { let new_flags = access::read_path(); // TODO: libnss exec perms? - for path in &["/etc/resolv.conf", "/etc/hosts", "/etc/host.conf", "/etc/nsswitch.conf", "/etc/gai.conf"] { + for path in &[ + "/etc/resolv.conf", + "/etc/hosts", + "/etc/host.conf", + "/etc/nsswitch.conf", + "/etc/gai.conf", + ] { self.insert_flags(path, new_flags); } // allow relevant syscalls as well self.allow_close() .allow_read() .allow_metadata() - .allow_open().yes_really() + .allow_open() + .yes_really() } } diff --git a/src/builtins/time.rs b/src/builtins/time.rs index a9b9ec6..472c960 100644 --- a/src/builtins/time.rs +++ b/src/builtins/time.rs @@ -1,11 +1,11 @@ //! Contains a [`RuleSet`] for allowing time-related syscalls, but check the comments for why you //! probably don't actually need to enable them. -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use syscalls::Sysno; -use crate::{SeccompRule, RuleSet}; +use crate::RuleSet; #[must_use] /// Enable syscalls related to time. @@ -22,10 +22,10 @@ impl Time { } } -/// On most 64 bit systems glibc and musl both use the -/// [`vDSO`](https://man7.org/linux/man-pages/man7/vdso.7.html) to compute the time directly with -/// rdtsc rather than calling the `clock_gettime` syscall, so in most cases you don't need to -/// actually enable this. + /// On most 64 bit systems glibc and musl both use the + /// [`vDSO`](https://man7.org/linux/man-pages/man7/vdso.7.html) to compute the time directly with + /// rdtsc rather than calling the `clock_gettime` syscall, so in most cases you don't need to + /// actually enable this. pub fn allow_gettime(mut self) -> Time { self.allowed .extend([Sysno::clock_gettime, Sysno::clock_getres]); diff --git a/src/error.rs b/src/error.rs index 2c65291..79e5944 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,10 +7,10 @@ use std::path::PathBuf; use seccompiler::Error as SeccompilerError; -#[cfg(feature = "landlock")] -use landlock::RulesetError as LandlockError; #[cfg(feature = "landlock")] use landlock::PathFdError; +#[cfg(feature = "landlock")] +use landlock::RulesetError as LandlockError; #[derive(Debug)] /// The error type produced by [`crate::SafetyContext`] diff --git a/src/landlock.rs b/src/landlock.rs index 3ce20a8..aa33e6f 100644 --- a/src/landlock.rs +++ b/src/landlock.rs @@ -5,7 +5,10 @@ use std::path::{Path, PathBuf}; pub use landlock::RulesetError as LandlockError; -pub use landlock::{ABI, Access, AccessFs, BitFlags, Compatible, CompatLevel, PathBeneath, PathFd, Ruleset, RulesetAttr, RulesetCreatedAttr}; +pub use landlock::{ + Access, AccessFs, BitFlags, CompatLevel, Compatible, PathBeneath, PathFd, Ruleset, RulesetAttr, + RulesetCreatedAttr, ABI, +}; /// A Landlock rule. It consists of a path and a collection of access rights which determine what /// actions can be performed on that path. @@ -21,10 +24,7 @@ impl LandlockRule { /// Create a new Landlock Rule. pub fn new>(path: P, access_rules: BitFlags) -> LandlockRule { let path = path.as_ref().into(); - LandlockRule { - path, - access_rules - } + LandlockRule { path, access_rules } } } diff --git a/src/lib.rs b/src/lib.rs index c09c860..849f47b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,18 +13,17 @@ //! See the [`SafetyContext`] struct's documentation and the tests/ and examples/ directories for //! more information on how to use it. - // Filter is the entire, top-level seccomp filter chain. All SeccompilerRules are or-ed together. // Vec<(i64, Vec)>, Vec is empty if Rule has no filters. // Rule is a syscall + multiple argument filters. All argument filters are and-ed together in a // single Rule. // ArgumentFilter is a single condition on a single argument // Comparator is used in an ArgumentFilter to choose the comparison operation -pub use seccompiler::SeccompFilter as SeccompilerFilter; -pub use seccompiler::SeccompRule as SeccompilerRule; -pub use seccompiler::SeccompCondition as SeccompilerArgumentFilter; pub use seccompiler::Error as SeccompilerError; pub use seccompiler::SeccompCmpOp as SeccompilerComparator; +pub use seccompiler::SeccompCondition as SeccompilerArgumentFilter; +pub use seccompiler::SeccompFilter as SeccompilerFilter; +pub use seccompiler::SeccompRule as SeccompilerRule; use seccompiler::SeccompAction; @@ -44,9 +43,9 @@ mod landlock; #[cfg(feature = "landlock")] pub use landlock::*; +use std::collections::{BTreeMap, HashMap}; #[cfg(feature = "landlock")] use std::path::PathBuf; -use std::collections::{BTreeMap, HashMap}; #[derive(Debug, Clone, PartialEq)] /// A restriction on the arguments of a syscall. May be combined with other @@ -87,7 +86,11 @@ impl SeccompArgumentFilter { #[must_use] /// Create a new [`SeccompArgumentFilter`]. You should probably use the [`seccomp_arg_filter!`] /// instead. - pub fn new(arg_idx: u8, comparator: SeccompilerComparator, value: u64) -> SeccompArgumentFilter { + pub fn new( + arg_idx: u8, + comparator: SeccompilerComparator, + value: u64, + ) -> SeccompArgumentFilter { // TODO: add quirks mode file and check whether syscall's parameter at index `arg_idx` is // 32 or 64 bit (and also I guess if it even has that many arguments) SeccompArgumentFilter::new64(arg_idx, comparator, value) @@ -96,7 +99,11 @@ impl SeccompArgumentFilter { #[must_use] /// Create a new [`SeccompArgumentFilter`] that checks all 64 bits of the provided argument. /// You should probably use the [`seccomp_arg_filter!`] instead. - pub fn new64(arg_idx: u8, comparator: SeccompilerComparator, value: u64) -> SeccompArgumentFilter { + pub fn new64( + arg_idx: u8, + comparator: SeccompilerComparator, + value: u64, + ) -> SeccompArgumentFilter { SeccompArgumentFilter { arg_idx, comparator, @@ -109,7 +116,11 @@ impl SeccompArgumentFilter { /// Create a new [`SeccompArgumentFilter`] that checks 32 bits of the provided argument. /// You should probably use the [`seccomp_arg_filter!`] instead. See the struct's documentation /// for why this is needed. - pub fn new32(arg_idx: u8, comparator: SeccompilerComparator, value: u32) -> SeccompArgumentFilter { + pub fn new32( + arg_idx: u8, + comparator: SeccompilerComparator, + value: u32, + ) -> SeccompArgumentFilter { // Note that it doesn't matter if we convert with or without sign extension here since the // point is that we'll only compare the least significant 32 bits anyway. let value = u64::from(value); @@ -123,9 +134,17 @@ impl SeccompArgumentFilter { pub(crate) fn into_seccompiler(self) -> Result { use seccompiler::SeccompCmpArgLen; - let arg_len = if self.is_64bit { SeccompCmpArgLen::Qword } else { SeccompCmpArgLen::Dword }; - Ok(SeccompilerArgumentFilter::new(self.arg_idx, arg_len, - self.comparator, self.value)?) + let arg_len = if self.is_64bit { + SeccompCmpArgLen::Qword + } else { + SeccompCmpArgLen::Dword + }; + Ok(SeccompilerArgumentFilter::new( + self.arg_idx, + arg_len, + self.comparator, + self.value, + )?) } } @@ -165,8 +184,11 @@ impl SeccompRule { return Ok(None); } - let argument_filters: Vec = self.argument_filters.into_iter() - .map(SeccompArgumentFilter::into_seccompiler).collect::>()?; + let argument_filters: Vec = self + .argument_filters + .into_iter() + .map(SeccompArgumentFilter::into_seccompiler) + .collect::>()?; Ok(Some(SeccompilerRule::new(argument_filters)?)) } @@ -291,13 +313,10 @@ impl SafetyContext { let mut rules = rules.conditional_rules(); for syscall in base_syscalls { let rule = SeccompRule::new(syscall); - rules.entry(syscall) - .or_insert_with(Vec::new) - .push(rule); + rules.entry(syscall).or_insert_with(Vec::new).push(rule); } - rules.into_values().flatten() - .collect() + rules.into_values().flatten().collect() } /// Enable the simple and conditional rules provided by the [`RuleSet`]. @@ -317,12 +336,18 @@ impl SafetyContext { #[cfg(feature = "landlock")] fn enable_landlock_rules(&mut self, policy: &R) -> Result<(), ExtraSafeError> { let name = policy.name(); - let rules = policy.landlock_rules().into_iter() + let rules = policy + .landlock_rules() + .into_iter() .map(|rule| (rule.path.clone(), LabeledLandlockRule(name, rule))); for (path, labeled_rule) in rules { if let Some(existing_rule) = self.landlock_rules.get(&path) { - return Err(ExtraSafeError::DuplicatePath(path.clone(), existing_rule.0, labeled_rule.0)); + return Err(ExtraSafeError::DuplicatePath( + path.clone(), + existing_rule.0, + labeled_rule.0, + )); } // value here is always none because we checked above that we're not inserting a path // that already exists @@ -356,8 +381,7 @@ impl SafetyContext { labeled_existing_rule.0, labeled_new_rule.0, )); - } - else if !new_is_simple && existing_is_simple { + } else if !new_is_simple && existing_is_simple { return Err(ExtraSafeError::ConditionalNoEffectError( new_rule.syscall, labeled_new_rule.0, @@ -525,7 +549,10 @@ impl SafetyContext { // should be allowed without restriction } let result = rules_map.insert(syscall, seccompiler_rules); - assert!(result.is_none(), "extrasafe logic error: somehow inserted the same syscall's rules twice"); + assert!( + result.is_none(), + "extrasafe logic error: somehow inserted the same syscall's rules twice" + ); } #[cfg(not(all(target_arch = "x86_64", target_os = "linux")))] @@ -535,15 +562,16 @@ impl SafetyContext { rules_map, SeccompAction::Errno(self.errno), SeccompAction::Allow, - std::env::consts::ARCH.try_into().expect("invalid arches are prevented above"), + std::env::consts::ARCH + .try_into() + .expect("invalid arches are prevented above"), )?; let bpf_filter: seccompiler::BpfProgram = seccompiler_filter.try_into()?; if self.all_threads { seccompiler::apply_filter_all_threads(&bpf_filter)?; - } - else { + } else { seccompiler::apply_filter(&bpf_filter)?; } @@ -552,11 +580,11 @@ impl SafetyContext { #[cfg(feature = "landlock")] fn apply_landlock_rules(&self) -> Result<(), ExtraSafeError> { - let abi = ABI::V2; - let mut landlock_ruleset = Ruleset::default() + let abi = ABI::V2; + let mut landlock_ruleset = Ruleset::default() .set_compatibility(CompatLevel::HardRequirement) - .handle_access(AccessFs::from_all(abi))? - .create()?; + .handle_access(AccessFs::from_all(abi))? + .create()?; for LabeledLandlockRule(_policy_name, rule) in self.landlock_rules.values() { // If path does not exist or is not accessible, just ignore it @@ -565,7 +593,7 @@ impl SafetyContext { landlock_ruleset = landlock_ruleset.add_rule(path_beneath)?; } } - let _status = landlock_ruleset.restrict_self(); + let _status = landlock_ruleset.restrict_self(); Ok(()) } } diff --git a/src/macros.rs b/src/macros.rs index 242c645..b9d961f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,40 +19,86 @@ #[macro_export] macro_rules! seccomp_arg_filter { ($argno:ident <= $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Le, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Le, + $value, + ) }; ($argno:ident < $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Lt, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Lt, + $value, + ) }; ($argno:ident >= $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Ge, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Ge, + $value, + ) }; ($argno:ident > $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Gt, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Gt, + $value, + ) }; ($argno:ident == $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Eq, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Eq, + $value, + ) }; ($argno:ident != $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Ne, $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::Ne, + $value, + ) }; ($argno:ident & $mask:tt == $value:expr) => { - $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::MaskedEq($mask), $value) + $crate::SeccompArgumentFilter::new( + match_argno!($argno), + $crate::SeccompilerComparator::MaskedEq($mask), + $value, + ) + }; + ($_other:expr) => { + compile_error!( + "usage: `arg[0-5] {<=, <, >=, >, ==, !=} ` or `arg[0-5] & == `" + ) }; - ($_other:expr) => {compile_error!("usage: `arg[0-5] {<=, <, >=, >, ==, !=} ` or `arg[0-5] & == `")}; } #[doc(hidden)] #[macro_export] /// Internal macro for `seccomp_arg_filter!` macro_rules! match_argno { - (arg0) => {0}; - (arg1) => {1}; - (arg2) => {2}; - (arg3) => {3}; - (arg4) => {4}; - (arg5) => {5}; - ($_other:expr) => {compile_error!("Seccomp argument filters must start with argX where X is 0-5")}; + (arg0) => { + 0 + }; + (arg1) => { + 1 + }; + (arg2) => { + 2 + }; + (arg3) => { + 3 + }; + (arg4) => { + 4 + }; + (arg5) => { + 5 + }; + ($_other:expr) => { + compile_error!("Seccomp argument filters must start with argX where X is 0-5") + }; } /// These tests just test that the macro expands correctly, not that the comparators do what they diff --git a/tests/arg_comparisons.rs b/tests/arg_comparisons.rs index f233c22..30db494 100644 --- a/tests/arg_comparisons.rs +++ b/tests/arg_comparisons.rs @@ -1,8 +1,8 @@ #![allow(unsafe_code)] // allow unsafe to call syscalls directly -use extrasafe::*; use builtins::SystemIO; +use extrasafe::*; use syscalls::Sysno; use std::collections::HashMap; @@ -16,8 +16,7 @@ impl RuleSet for IoctlRestricted64 { fn conditional_rules(&self) -> HashMap> { let cmp = SeccompArgumentFilter::new64(1, SeccompilerComparator::Eq, self.0); - let rule = SeccompRule::new(Sysno::ioctl) - .and_condition(cmp); + let rule = SeccompRule::new(Sysno::ioctl).and_condition(cmp); HashMap::from([(Sysno::ioctl, vec![rule])]) } @@ -36,8 +35,7 @@ impl RuleSet for IoctlRestricted32 { fn conditional_rules(&self) -> HashMap> { let cmp = SeccompArgumentFilter::new32(1, SeccompilerComparator::Eq, self.0); - let rule = SeccompRule::new(Sysno::ioctl) - .and_condition(cmp); + let rule = SeccompRule::new(Sysno::ioctl).and_condition(cmp); HashMap::from([(Sysno::ioctl, vec![rule])]) } @@ -47,7 +45,6 @@ impl RuleSet for IoctlRestricted32 { } } - struct GetUidRestricted; impl RuleSet for GetUidRestricted { fn simple_rules(&self) -> Vec { @@ -77,11 +74,12 @@ fn cmp_arg_syscall_unused_parameter() { assert!(uid1 > 0); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .enable(GetUidRestricted).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(GetUidRestricted) + .unwrap() + .apply_to_current_thread() + .unwrap(); // SAFETY: getuid just gives the current user's uid let uid2 = unsafe { libc::getuid() }; @@ -92,7 +90,7 @@ macro_rules! assert_errno { ($e: expr) => { let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); assert_eq!(errno, $e); - } + }; } // See https://github.com/rust-vmm/seccompiler/issues/59 for more details on below two tests @@ -102,11 +100,12 @@ fn cmp_arg_64bit_ioctl_musl_glibc_diff() { let seccomp_errno = 999; extrasafe::SafetyContext::new() .with_errno(seccomp_errno) - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .enable(IoctlRestricted64(value)).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(IoctlRestricted64(value)) + .unwrap() + .apply_to_current_thread() + .unwrap(); // On glibc, the second parameter is a u64, so the value seen by the kernel matches the value // in our seccomp filter, and the ioctl call is allowed. It then sets errno to -9 since 4321 is @@ -134,11 +133,12 @@ fn cmp_arg_32bit_ioctl_musl_glibc_same() { let seccomp_errno = 999; extrasafe::SafetyContext::new() .with_errno(seccomp_errno) - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .enable(IoctlRestricted32(value)).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(IoctlRestricted32(value)) + .unwrap() + .apply_to_current_thread() + .unwrap(); // here both tests are the same except for value being i32 on musl #[cfg(target_env = "gnu")] diff --git a/tests/bad_combination.rs b/tests/bad_combination.rs index b44071b..964f8c8 100644 --- a/tests/bad_combination.rs +++ b/tests/bad_combination.rs @@ -24,8 +24,8 @@ impl RuleSet for JustWrite { /// (This is because the simple rule would override the conditional one) fn invalid_combination_new_simple() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout()).unwrap() + .enable(SystemIO::nothing().allow_stdout()) + .unwrap() .enable(SystemIO::everything()); assert!( @@ -40,10 +40,13 @@ fn invalid_combination_new_simple() { #[test] fn invalid_combination_new_conditional() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::everything()).unwrap() - .enable(SystemIO::nothing() - .allow_stdout()); - assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); + .enable(SystemIO::everything()) + .unwrap() + .enable(SystemIO::nothing().allow_stdout()); + assert!( + res.is_err(), + "Extrasafe didn't fail when adding conflicting rules" + ); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `write` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `SystemIO`."); @@ -53,8 +56,8 @@ fn invalid_combination_new_conditional() { /// same as above but with different rulesets to check the error message fn invalid_combination_new_simple_different_name() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout()).unwrap() + .enable(SystemIO::nothing().allow_stdout()) + .unwrap() .enable(JustWrite); assert!( res.is_err(), @@ -68,10 +71,13 @@ fn invalid_combination_new_simple_different_name() { #[test] fn invalid_combination_new_conditional_different_name() { let res = extrasafe::SafetyContext::new() - .enable(JustWrite).unwrap() - .enable(SystemIO::nothing() - .allow_stdout()); - assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); + .enable(JustWrite) + .unwrap() + .enable(SystemIO::nothing().allow_stdout()); + assert!( + res.is_err(), + "Extrasafe didn't fail when adding conflicting rules" + ); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `write` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `JustWrite`."); @@ -80,13 +86,12 @@ fn invalid_combination_new_conditional_different_name() { #[test] /// Test that adding a conditional and simple rule in the same `RuleSet` produces an error fn invalid_combination_read_and_stdin() { - - let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_read() - .allow_stdin() - ); - assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); + let res = + extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_read().allow_stdin()); + assert!( + res.is_err(), + "Extrasafe didn't fail when adding conflicting rules" + ); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `read` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `SystemIO`."); @@ -95,12 +100,7 @@ fn invalid_combination_read_and_stdin() { #[test] /// Test that adding duplicate simple rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_simple() { - - let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_read() - .allow_read() - ); + let res = extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_read().allow_read()); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -110,13 +110,10 @@ fn not_invalid_combination_duplicate_simple() { #[test] /// Test that adding duplicate simple rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_simple2() { - let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_read()).unwrap() - .enable(SystemIO::nothing() - .allow_read() - ); + .enable(SystemIO::nothing().allow_read()) + .unwrap() + .enable(SystemIO::nothing().allow_read()); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -126,12 +123,8 @@ fn not_invalid_combination_duplicate_simple2() { #[test] /// Test that adding duplicate conditional rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_conditional() { - - let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdin() - .allow_stdin() - ); + let res = + extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_stdin().allow_stdin()); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -141,14 +134,10 @@ fn not_invalid_combination_duplicate_conditional() { #[test] /// Test that adding duplicate conditional rules from different `RuleSet`s doesn't produce an error fn not_invalid_combination_duplicate_conditional2() { - let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdin() - ).unwrap() - .enable(SystemIO::nothing() - .allow_stdin() - ); + .enable(SystemIO::nothing().allow_stdin()) + .unwrap() + .enable(SystemIO::nothing().allow_stdin()); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); diff --git a/tests/default_deny.rs b/tests/default_deny.rs index 0efa8da..c84cea2 100644 --- a/tests/default_deny.rs +++ b/tests/default_deny.rs @@ -22,7 +22,8 @@ fn filesystem_no_read() { drop(file); let res = extrasafe::SafetyContext::new() - .enable(Basic).unwrap() + .enable(Basic) + .unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); @@ -47,7 +48,8 @@ fn filesystem_no_write() { let mut file = tempfile().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(Basic).unwrap() + .enable(Basic) + .unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); @@ -72,7 +74,8 @@ fn filesystem_no_create() { let dir = tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(Basic).unwrap() + .enable(Basic) + .unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); diff --git a/tests/inherit_filters.rs b/tests/inherit_filters.rs index fdf5ca2..31bfb72 100644 --- a/tests/inherit_filters.rs +++ b/tests/inherit_filters.rs @@ -1,15 +1,14 @@ //! Tests that demonstrate seccomp filters are inherited by child processes. -use extrasafe::SafetyContext; use extrasafe::builtins::danger_zone::{ForkAndExec, Threads}; +use extrasafe::SafetyContext; #[test] /// Enable seccomp *only on this thread*, create a new thread, try to create a file and check that /// it fails. fn new_thread_inherits_restrictions() { SafetyContext::new() - .enable(Threads::nothing() - .allow_create()) + .enable(Threads::nothing().allow_create()) .unwrap() .apply_to_current_thread() .unwrap(); diff --git a/tests/landlock_allthreads_fail.rs b/tests/landlock_allthreads_fail.rs index 5a26890..d4cf1dd 100644 --- a/tests/landlock_allthreads_fail.rs +++ b/tests/landlock_allthreads_fail.rs @@ -9,13 +9,17 @@ fn test_landlock_apply_to_all_fails() { let dir = tempfile::tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_read_path(&dir) - ).unwrap() + .enable(SystemIO::nothing().allow_read_path(&dir)) + .unwrap() .landlock_only() .apply_to_all_threads(); - assert!(res.is_err(), "Did not error when applying to all threads with landlock rules"); - assert!(res.unwrap_err().to_string().contains("Landlock does not support syncing to all threads")); + assert!( + res.is_err(), + "Did not error when applying to all threads with landlock rules" + ); + assert!(res + .unwrap_err() + .to_string() + .contains("Landlock does not support syncing to all threads")); } diff --git a/tests/landlock_basic.rs b/tests/landlock_basic.rs index 05c4fce..09c51f7 100644 --- a/tests/landlock_basic.rs +++ b/tests/landlock_basic.rs @@ -2,15 +2,19 @@ use std::path::Path; -use std::io::{Read, Write}; use std::fs::{create_dir, read_dir, remove_dir, remove_file, File}; +use std::io::{Read, Write}; use extrasafe::builtins::SystemIO; /// helper to check a file can be read fn can_read_file(path: &Path, expected_data: &str) { let res = File::open(path); - assert!(res.is_ok(), "Failed to open allowed file: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to open allowed file: {:?}", + res.unwrap_err() + ); let mut f = res.unwrap(); let mut file_contents = String::new(); @@ -23,12 +27,20 @@ fn can_read_file(path: &Path, expected_data: &str) { /// helper to check a file can be written to fn can_write_file(path: &Path, write_data: &str) { let res = File::options().append(true).open(path); - assert!(res.is_ok(), "Failed to open allowed file: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to open allowed file: {:?}", + res.unwrap_err() + ); let mut f = res.unwrap(); let res = f.write_all(write_data.as_bytes()); - assert!(res.is_ok(), "failed to write to file: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "failed to write to file: {:?}", + res.unwrap_err() + ); drop(f); // after writing the data, check we can read it back @@ -38,7 +50,10 @@ fn can_write_file(path: &Path, write_data: &str) { /// helper to check a file cannot be opened fn can_not_open_file(path: &Path) { let res = File::open(path); - assert!(res.is_err(), "Incorrectly succeeded in opening file for reading"); + assert!( + res.is_err(), + "Incorrectly succeeded in opening file for reading" + ); } // TODO: distinguish between not being able to remove dir due to not being empty vs denied via @@ -65,11 +80,10 @@ fn test_landlock_read_file() { drop(f); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_read_path(&allowed_file) - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_read_path(&allowed_file)) + .unwrap() + .apply_to_current_thread() + .unwrap(); // read allowed, fail to open denied can_read_file(&allowed_file, "test allowed"); @@ -89,9 +103,11 @@ fn test_landlock_write_file() { .enable( SystemIO::nothing() .allow_write_file(&dir) - .allow_read_path(&dir) - ).unwrap() - .apply_to_current_thread().unwrap(); + .allow_read_path(&dir), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); can_write_file(&allowed_file, "test data"); @@ -110,18 +126,24 @@ fn test_landlock_create_file_in_path() { let denied_file = dir_denied.path().join("denied.txt"); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_create_in_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_create_in_dir(&dir_allowed)) + .unwrap() + .apply_to_current_thread() + .unwrap(); // create succeeds in one directory, fails in other let res = File::create(allowed_file); - assert!(res.is_ok(), "Failed to create file in allowed directory: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to create file in allowed directory: {:?}", + res.unwrap_err() + ); let res = File::create(denied_file); - assert!(res.is_err(), "Incorrectly suceeded in creating file in directory we did not allow"); + assert!( + res.is_err(), + "Incorrectly suceeded in creating file in directory we did not allow" + ); } #[test] @@ -142,9 +164,11 @@ fn test_landlock_delete_file() { .enable( SystemIO::nothing() .allow_remove_file(&dir_allowed) - .allow_list_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .allow_list_dir(&dir_allowed), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); let res = remove_file(&allowed_file); assert!(res.is_ok(), "Failed to remove file: {}", res.unwrap_err()); @@ -153,7 +177,10 @@ fn test_landlock_delete_file() { assert_eq!(dir.collect::>().len(), 0); let res = remove_file(&denied_file); - assert!(res.is_err(), "Incorrectly succeeded in removing file that was not allowed"); + assert!( + res.is_err(), + "Incorrectly succeeded in removing file that was not allowed" + ); } #[test] @@ -175,11 +202,10 @@ fn test_landlock_read_dir() { drop(f); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_read_path(allowed_subdir) - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_read_path(allowed_subdir)) + .unwrap() + .apply_to_current_thread() + .unwrap(); // read allowed, fail to open denied can_read_file(&allowed_file, "test allowed"); @@ -193,11 +219,10 @@ fn test_landlock_create_dir() { let dir_denied = tempfile::tempdir().unwrap(); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_create_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_create_dir(&dir_allowed)) + .unwrap() + .apply_to_current_thread() + .unwrap(); let allowed_subdir = dir_allowed.path().join("test_allowed"); let res = create_dir(allowed_subdir); @@ -219,20 +244,26 @@ fn test_landlock_list_dir() { drop(f); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_list_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_list_dir(&dir_allowed)) + .unwrap() + .apply_to_current_thread() + .unwrap(); let res = read_dir(&dir_allowed); - assert!(res.is_ok(), "Failed to list directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to list directory: {}", + res.unwrap_err() + ); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); let res = read_dir(&dir_denied); - assert!(res.is_err(), "Incorrectly succeeded in reading directory that was not allowed"); + assert!( + res.is_err(), + "Incorrectly succeeded in reading directory that was not allowed" + ); } #[test] @@ -251,15 +282,24 @@ fn test_landlock_delete_dir() { .enable( SystemIO::nothing() .allow_remove_dir(&dir_allowed) - .allow_list_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .allow_list_dir(&dir_allowed), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); let res = remove_dir(&allowed_subdir); - assert!(res.is_ok(), "Failed to remove directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to remove directory: {}", + res.unwrap_err() + ); let res = remove_dir(&denied_subdir); - assert!(res.is_err(), "Incorrectly succeeded in removing directory that was not allowed"); + assert!( + res.is_err(), + "Incorrectly succeeded in removing directory that was not allowed" + ); // check dir is empty let mut dir = read_dir(&dir_allowed).unwrap(); @@ -289,9 +329,11 @@ fn test_landlock_one_ruleset() { .allow_list_dir(&allowed_subdir_write) .allow_read_path(&allowed_subdir_write) .allow_write_file(&allowed_subdir_write) - .allow_remove_dir(&dir_allowed) - ).unwrap() - .apply_to_current_thread().unwrap(); + .allow_remove_dir(&dir_allowed), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); // create file in write dir let allowed_file = allowed_subdir_write.as_path().join("allowed.txt"); @@ -301,19 +343,31 @@ fn test_landlock_one_ruleset() { // check we can list ro directory let res = read_dir(&allowed_subdir); - assert!(res.is_ok(), "Failed to list ro directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to list ro directory: {}", + res.unwrap_err() + ); let mut dir = res.unwrap(); assert!(dir.next().is_none()); // check we can list rw directory let res = read_dir(&allowed_subdir_write); - assert!(res.is_ok(), "Failed to list rw directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to list rw directory: {}", + res.unwrap_err() + ); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); // check we can remove ro directory (even though we can't write to it!) let res = remove_dir(&allowed_subdir); - assert!(res.is_ok(), "Failed to remove ro directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to remove ro directory: {}", + res.unwrap_err() + ); // check we can read file we wrote to can_read_file(&allowed_file, "test allowed write directory"); @@ -342,15 +396,18 @@ fn test_landlock_different_rulesets() { .allow_list_dir(&allowed_subdir) .allow_read_path(&allowed_subdir) .allow_remove_dir(&dir_allowed) - .allow_list_dir(&dir_allowed) - ).unwrap() + .allow_list_dir(&dir_allowed), + ) + .unwrap() .enable( SystemIO::nothing() .allow_create_in_dir(&allowed_subdir_write) .allow_read_path(&allowed_subdir_write) - .allow_write_file(&allowed_subdir_write) - ).unwrap() - .apply_to_current_thread().unwrap(); + .allow_write_file(&allowed_subdir_write), + ) + .unwrap() + .apply_to_current_thread() + .unwrap(); // create file in write dir let allowed_file = allowed_subdir_write.as_path().join("allowed.txt"); @@ -360,19 +417,31 @@ fn test_landlock_different_rulesets() { // check we can list ro directory let res = read_dir(&allowed_subdir); - assert!(res.is_ok(), "Failed to list ro directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to list ro directory: {}", + res.unwrap_err() + ); let mut dir = res.unwrap(); assert!(dir.next().is_none()); // check we can list rw directory let res = read_dir(&allowed_subdir_write); - assert!(res.is_ok(), "Failed to list rw directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to list rw directory: {}", + res.unwrap_err() + ); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); // check we can remove ro directory (even though we can't write to it!) let res = remove_dir(&allowed_subdir); - assert!(res.is_ok(), "Failed to remove ro directory: {}", res.unwrap_err()); + assert!( + res.is_ok(), + "Failed to remove ro directory: {}", + res.unwrap_err() + ); // check we can read file we wrote to can_read_file(&allowed_file, "test allowed write directory"); @@ -388,14 +457,16 @@ fn test_nonexistant_file_no_error() { let nonexistant = dir.path().join("bad"); let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_create_in_dir(nonexistant) - ).unwrap() + .enable(SystemIO::nothing().allow_create_in_dir(nonexistant)) + .unwrap() .landlock_only() .apply_to_current_thread(); - assert!(res.is_ok(), "Errored when passing nonexistant file to landlock rule: {:?} ", res.unwrap_err()); + assert!( + res.is_ok(), + "Errored when passing nonexistant file to landlock rule: {:?} ", + res.unwrap_err() + ); } #[test] @@ -404,17 +475,17 @@ fn test_duplicate_path() { let dir = tempfile::tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_create_in_dir(&dir) - ).unwrap() - .enable( - SystemIO::nothing() - .allow_create_in_dir(&dir) - ); - - assert!(res.is_err(), "Did not error on passing same dir in multiple rulesets"); + .enable(SystemIO::nothing().allow_create_in_dir(&dir)) + .unwrap() + .enable(SystemIO::nothing().allow_create_in_dir(&dir)); + + assert!( + res.is_err(), + "Did not error on passing same dir in multiple rulesets" + ); let err = res.unwrap_err(); assert!(err.to_string().contains("The same path")); - assert!(err.to_string().contains("was used in two different landlock rules.")); + assert!(err + .to_string() + .contains("was used in two different landlock rules.")); } diff --git a/tests/landlock_conflicts.rs b/tests/landlock_conflicts.rs index 52400c8..c4743bc 100644 --- a/tests/landlock_conflicts.rs +++ b/tests/landlock_conflicts.rs @@ -1,6 +1,6 @@ #![cfg(feature = "landlock")] -use std::fs::{File, read_dir}; +use std::fs::{read_dir, File}; use extrasafe::builtins::SystemIO; @@ -11,46 +11,44 @@ fn landlock_with_seccomp_arg_filters_fails() { let path = tempfile::tempdir().unwrap(); // same ruleset - let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_open_readonly() - .allow_list_dir(&path) - ); - - assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); + let res = extrasafe::SafetyContext::new().enable( + SystemIO::nothing() + .allow_open_readonly() + .allow_list_dir(&path), + ); + + assert!( + res.is_err(), + "Enabling filter succeeded with landlock and seccomp arg-restricted open" + ); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert_eq!(err.to_string().contains("xxx")); // different rulesets, landlock first let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_open_readonly() - ).unwrap() - .enable( - SystemIO::nothing() - .allow_read_path(&path) - ); - - assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); + .enable(SystemIO::nothing().allow_open_readonly()) + .unwrap() + .enable(SystemIO::nothing().allow_read_path(&path)); + + assert!( + res.is_err(), + "Enabling filter succeeded with landlock and seccomp arg-restricted open" + ); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert!(err.to_string().contains("xxx")); // different rulesets, seccomp first let res = extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_read_path(&path) - ).unwrap() - .enable( - SystemIO::nothing() - .allow_open_readonly() - ); - - assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); + .enable(SystemIO::nothing().allow_read_path(&path)) + .unwrap() + .enable(SystemIO::nothing().allow_open_readonly()); + + assert!( + res.is_err(), + "Enabling filter succeeded with landlock and seccomp arg-restricted open" + ); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert!(err.to_string().contains("xxx")); @@ -64,7 +62,10 @@ fn landlock_only() { .landlock_only() .apply_to_current_thread(); - assert!(res.is_err(), "extrasafe did not error when applying with no seccomp or landlock rules"); + assert!( + res.is_err(), + "extrasafe did not error when applying with no seccomp or landlock rules" + ); let err = res.unwrap_err(); assert!(err.to_string().contains("No rules were enabled")); @@ -72,12 +73,11 @@ fn landlock_only() { let dir = tempfile::tempdir().unwrap(); extrasafe::SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_create_in_dir(&dir) - ).unwrap() + .enable(SystemIO::nothing().allow_create_in_dir(&dir)) + .unwrap() .landlock_only() - .apply_to_current_thread().unwrap(); + .apply_to_current_thread() + .unwrap(); // test that we can run arbitrary syscalls let pid = unsafe { libc::getpid() }; @@ -86,11 +86,21 @@ fn landlock_only() { // test that we can create in the given directory let file_path = dir.path().join("okay.txt"); let file_res = File::create(file_path); - assert!(file_res.is_ok(), "Failed to create file in allowed dir: {:?}", file_res.unwrap_err()); + assert!( + file_res.is_ok(), + "Failed to create file in allowed dir: {:?}", + file_res.unwrap_err() + ); // test that we can't list paths let list_res = read_dir(&dir); - assert!(list_res.is_err(), "Incorrectly succeeded in listing directory"); + assert!( + list_res.is_err(), + "Incorrectly succeeded in listing directory" + ); let list_res = read_dir("/etc"); - assert!(list_res.is_err(), "Incorrectly succeeded in listing directory"); + assert!( + list_res.is_err(), + "Incorrectly succeeded in listing directory" + ); } diff --git a/tests/multiple_conditions.rs b/tests/multiple_conditions.rs index 298ff34..f92aa22 100644 --- a/tests/multiple_conditions.rs +++ b/tests/multiple_conditions.rs @@ -7,16 +7,23 @@ use extrasafe::*; /// or-ed together across all `RuleSets`. fn multiple_rulsets_conditional() { SafetyContext::new() - .enable(builtins::SystemIO::nothing() - .allow_stdout() - ).unwrap() - .enable(builtins::SystemIO::nothing() - .allow_stderr() - ).unwrap() - .apply_to_current_thread().unwrap(); + .enable(builtins::SystemIO::nothing().allow_stdout()) + .unwrap() + .enable(builtins::SystemIO::nothing().allow_stderr()) + .unwrap() + .apply_to_current_thread() + .unwrap(); let res = writeln!(std::io::stdout(), "we can print to stdout"); - assert!(res.is_ok(), "failed to write to stdout: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "failed to write to stdout: {:?}", + res.unwrap_err() + ); let res = writeln!(std::io::stderr(), "we can print to stderr"); - assert!(res.is_ok(), "failed to write to stderr: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "failed to write to stderr: {:?}", + res.unwrap_err() + ); } diff --git a/tests/multiple_filters.rs b/tests/multiple_filters.rs index 6b7f233..3ab4790 100644 --- a/tests/multiple_filters.rs +++ b/tests/multiple_filters.rs @@ -1,5 +1,5 @@ -use extrasafe::*; use extrasafe::syscalls::Sysno; +use extrasafe::*; use std::collections::HashMap; @@ -22,23 +22,25 @@ impl RuleSet for Seccomp { /// really doesn't ever make sense to enable multiple filters. fn filter_stacking_works_but_may_give_unintended_results() { SafetyContext::new() - .enable(builtins::SystemIO::nothing() - .allow_stdout() - .allow_stderr() - .allow_open().yes_really() - .allow_metadata() - ).unwrap() - .enable(Seccomp).unwrap() - .apply_to_current_thread().unwrap(); + .enable( + builtins::SystemIO::nothing() + .allow_stdout() + .allow_stderr() + .allow_open() + .yes_really() + .allow_metadata(), + ) + .unwrap() + .enable(Seccomp) + .unwrap() + .apply_to_current_thread() + .unwrap(); let res = SafetyContext::new() - .enable(builtins::SystemIO::nothing() - .allow_stdout() - .allow_stderr() - ).unwrap() - .enable(builtins::danger_zone::Threads::nothing() - .allow_create() - ).unwrap() + .enable(builtins::SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(builtins::danger_zone::Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread(); assert!( res.is_ok(), @@ -47,8 +49,7 @@ fn filter_stacking_works_but_may_give_unintended_results() { ); println!("test"); - let res = std::thread::Builder::new() - .spawn(|| println!("will not run")); + let res = std::thread::Builder::new().spawn(|| println!("will not run")); assert!(res.is_err(), "Even though clone was enabled on the second filter, it was not in the first and so isn't allowed."); let res = std::fs::File::open("will_not_be_opened.txt"); diff --git a/tests/network.rs b/tests/network.rs index dc0b92e..150c01f 100644 --- a/tests/network.rs +++ b/tests/network.rs @@ -16,7 +16,6 @@ use std::thread; /// main thread, enable seccomp, send the message and get a response. Then try to bind a new socket /// and check that it fails. fn test_udp() { - // These block on send until reciever has finished recv. let (sender1, recv1) = sync_channel::<()>(0); @@ -47,14 +46,10 @@ fn test_udp() { // create safetycontext after server and client have been bound. SafetyContext::new() - .enable( - Networking::nothing() - .allow_running_udp_sockets() - ).unwrap() - .enable( - Threads::nothing() - .allow_create() - ).unwrap() + .enable(Networking::nothing().allow_running_udp_sockets()) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -115,11 +110,10 @@ fn test_tcp() { // create safetycontext after server and client have been bound. SafetyContext::new() - .enable(Networking::nothing() - .allow_running_tcp_clients() - ).unwrap() - .enable(Threads::nothing() - .allow_create()).unwrap() + .enable(Networking::nothing().allow_running_tcp_clients()) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -152,28 +146,30 @@ fn test_tcp() { /// instead open and bind before applying your policy. fn test_start_tcp() { SafetyContext::new() - .enable( - Networking::nothing() - .allow_start_tcp_servers().yes_really() - ).unwrap() - .enable( - Threads::nothing() - .allow_create() - ).unwrap() + .enable(Networking::nothing().allow_start_tcp_servers().yes_really()) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread() .unwrap(); let tcp_res = std::net::TcpListener::bind("127.0.0.1:0"); assert!(tcp_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); - assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); + assert!( + udp_res.is_err(), + "Incorrectly succeeded in binding udp socket" + ); // test ipv6 as well let tcp_res = std::net::TcpListener::bind("[::1]:0"); assert!(tcp_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("[::1]:0"); - assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); + assert!( + udp_res.is_err(), + "Incorrectly succeeded in binding udp socket" + ); } #[test] @@ -181,28 +177,30 @@ fn test_start_tcp() { /// instead open and bind before applying your policy. fn test_start_udp() { SafetyContext::new() - .enable( - Networking::nothing() - .allow_start_udp_servers().yes_really() - ).unwrap() - .enable( - Threads::nothing() - .allow_create() - ).unwrap() + .enable(Networking::nothing().allow_start_udp_servers().yes_really()) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread() .unwrap(); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); assert!(udp_res.is_ok(), "Failed to bind udp server"); let udp_res = std::net::TcpListener::bind("127.0.0.1:0"); - assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); + assert!( + udp_res.is_err(), + "Incorrectly succeeded in binding udp socket" + ); // test ipv6 as well let udp_res = std::net::UdpSocket::bind("[::1]:0"); assert!(udp_res.is_ok(), "Failed to bind udp server"); let tcp_res = std::net::TcpListener::bind("[::1]:0"); - assert!(tcp_res.is_err(), "Incorrectly succeeded in binding tcp socket"); + assert!( + tcp_res.is_err(), + "Incorrectly succeeded in binding tcp socket" + ); } #[test] @@ -216,12 +214,12 @@ fn test_start_unix() { SafetyContext::new() .enable( Networking::nothing() - .allow_start_unix_servers().yes_really() - ).unwrap() - .enable( - Threads::nothing() - .allow_create() - ).unwrap() + .allow_start_unix_servers() + .yes_really(), + ) + .unwrap() + .enable(Threads::nothing().allow_create()) + .unwrap() .apply_to_current_thread() .unwrap(); @@ -229,8 +227,14 @@ fn test_start_unix() { assert!(unix_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); - assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); + assert!( + udp_res.is_err(), + "Incorrectly succeeded in binding udp socket" + ); let tcp_res = std::net::TcpListener::bind("[::1]:0"); - assert!(tcp_res.is_err(), "Incorrectly succeeded in binding tcp socket"); + assert!( + tcp_res.is_err(), + "Incorrectly succeeded in binding tcp socket" + ); } diff --git a/tests/ruleset_union.rs b/tests/ruleset_union.rs index 2567bea..d2e9775 100644 --- a/tests/ruleset_union.rs +++ b/tests/ruleset_union.rs @@ -1,10 +1,9 @@ -use std::path::Path; use std::fs::File; use std::io::{Read, Write}; +use std::path::Path; -use extrasafe::*; use builtins::SystemIO; - +use extrasafe::*; // Tests to make sure we don't run into this issue // https://github.com/rust-vmm/seccompiler/issues/42 @@ -15,18 +14,20 @@ use builtins::SystemIO; fn different_rulesets_same_syscall() { SafetyContext::new() // First RuleSet: stdout, stderr - .enable(SystemIO::nothing() - .allow_read() - .allow_stdout() - .allow_stderr() - .allow_metadata() - ).unwrap() .enable( - // Second RuleSet: stderr only - SystemIO::nothing() - .allow_stderr() - .allow_metadata() - .allow_close(), + SystemIO::nothing() + .allow_read() + .allow_stdout() + .allow_stderr() + .allow_metadata(), + ) + .unwrap() + .enable( + // Second RuleSet: stderr only + SystemIO::nothing() + .allow_stderr() + .allow_metadata() + .allow_close(), ) .unwrap() .apply_to_current_thread() @@ -34,9 +35,17 @@ fn different_rulesets_same_syscall() { // Try to write to stdout and stderr let res = writeln!(std::io::stdout(), "we can print to stdout"); - assert!(res.is_ok(), "failed to write to stdout: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "failed to write to stdout: {:?}", + res.unwrap_err() + ); let res = writeln!(std::io::stderr(), "we can print to stderr"); - assert!(res.is_ok(), "failed to write to stderr: {:?}", res.unwrap_err()); + assert!( + res.is_ok(), + "failed to write to stderr: {:?}", + res.unwrap_err() + ); } fn create_testfile(path: &Path, filename: &str) -> File { @@ -51,7 +60,6 @@ fn create_testfile(path: &Path, filename: &str) -> File { File::open(&path).unwrap() } - #[test] /// Same as above but with mask instead of == and also 3 rulesets fn different_rulesets_same_syscall2() { @@ -68,19 +76,14 @@ fn different_rulesets_same_syscall2() { // Add three different rulesets each allowing reads to a different file SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr() - ).unwrap() - .enable(SystemIO::nothing() - .allow_file_read(&file1) - ).unwrap() - .enable(SystemIO::nothing() - .allow_file_read(&file2) - ).unwrap() - .enable(SystemIO::nothing() - .allow_file_read(&file3) - ).unwrap() + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(SystemIO::nothing().allow_file_read(&file1)) + .unwrap() + .enable(SystemIO::nothing().allow_file_read(&file2)) + .unwrap() + .enable(SystemIO::nothing().allow_file_read(&file3)) + .unwrap() .apply_to_current_thread() .unwrap(); diff --git a/tests/sleep.rs b/tests/sleep.rs index 4cc9766..fb9d91c 100644 --- a/tests/sleep.rs +++ b/tests/sleep.rs @@ -1,13 +1,13 @@ -use extrasafe::builtins::{SystemIO, danger_zone::Threads}; +use extrasafe::builtins::{danger_zone::Threads, SystemIO}; #[test] #[should_panic] fn insomnia() { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .apply_to_current_thread() + .unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); } @@ -15,12 +15,12 @@ fn insomnia() { #[test] fn comfy() { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .enable(Threads::nothing() - .allow_sleep().yes_really()).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .enable(Threads::nothing().allow_sleep().yes_really()) + .unwrap() + .apply_to_current_thread() + .unwrap(); std::thread::sleep(std::time::Duration::from_millis(1)); } diff --git a/tests/test_ref_ruleset.rs b/tests/test_ref_ruleset.rs index 7879846..af09176 100644 --- a/tests/test_ref_ruleset.rs +++ b/tests/test_ref_ruleset.rs @@ -1,9 +1,11 @@ -use extrasafe::RuleSet; use extrasafe::builtins::BasicCapabilities; +use extrasafe::RuleSet; #[test] /// Test if `RuleSets` can be references. fn ref_ruleset() -> Result<(), extrasafe::ExtraSafeError> { let ruleset: &dyn RuleSet = &BasicCapabilities; - extrasafe::SafetyContext::new().enable(ruleset)?.apply_to_current_thread() + extrasafe::SafetyContext::new() + .enable(ruleset)? + .apply_to_current_thread() } diff --git a/tests/tests_can_fail.rs b/tests/tests_can_fail.rs index b4f5872..099b383 100644 --- a/tests/tests_can_fail.rs +++ b/tests/tests_can_fail.rs @@ -5,8 +5,7 @@ /// This is also manually tested by commenting out the assert line and checking that the test /// failure propagates to the cli fn seccomp_active_tests_fail() { - let res = extrasafe::SafetyContext::new() - .apply_to_current_thread(); + let res = extrasafe::SafetyContext::new().apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); assert!(false, "should fail"); diff --git a/tests/thread_multi.rs b/tests/thread_multi.rs index 7b28213..fbb281f 100644 --- a/tests/thread_multi.rs +++ b/tests/thread_multi.rs @@ -16,10 +16,10 @@ fn sync_thread_contexts() { let seccomp_thread = thread::spawn(move || { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .apply_to_all_threads().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .apply_to_all_threads() + .unwrap(); // setup_done sender1.send(()).unwrap(); diff --git a/tests/thread_single.rs b/tests/thread_single.rs index 504fb71..7e5d462 100644 --- a/tests/thread_single.rs +++ b/tests/thread_single.rs @@ -16,17 +16,16 @@ use std::fs::File; /// threads. This is achieved in this test by blocking IO on one thread and not on another, and /// checking IO can be performed in the other thread after loading the context in the first. fn different_threads_with_different_contexts() { - // These channels will block on send until the receiver has called recv. let (sender1, recv1) = sync_channel::<()>(0); let (sender2, recv2) = sync_channel::<()>(0); let seccomp_thread = thread::spawn(move || { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing() - .allow_stdout() - .allow_stderr()).unwrap() - .apply_to_current_thread().unwrap(); + .enable(SystemIO::nothing().allow_stdout().allow_stderr()) + .unwrap() + .apply_to_current_thread() + .unwrap(); // setup_done sender1.send(()).unwrap(); diff --git a/tests/unsupported_os.rs b/tests/unsupported_os.rs index 3df8bc3..0daeb22 100644 --- a/tests/unsupported_os.rs +++ b/tests/unsupported_os.rs @@ -1,8 +1,7 @@ #[cfg(not(target_os = "linux"))] #[test] fn returns_unsupported_os_error() { - let res = extrasafe::SafetyContext::new() - .apply_to_all_threads(); + let res = extrasafe::SafetyContext::new().apply_to_all_threads(); assert!( res.is_err(), From 38de2f9210284b56abccd197f94c46380b8fea79 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 17 Mar 2024 12:36:47 +0100 Subject: [PATCH 02/25] Implement multiarch-support --- Cargo.toml | 3 +++ build.rs | 12 +++++++++++ src/builtins/basic.rs | 4 +++- src/builtins/danger_zone.rs | 10 ++++++++-- src/builtins/network.rs | 11 +++++++--- src/builtins/pipes.rs | 8 ++++++-- src/builtins/systemio.rs | 40 +++++++++++++++++++++++++++---------- src/builtins/time.rs | 2 +- src/error.rs | 2 +- src/lib.rs | 10 +++++----- src/syscalls.rs | 25 +++++++++++++++++++++++ tests/arg_comparisons.rs | 2 +- tests/sysno.rs | 2 +- 13 files changed, 103 insertions(+), 28 deletions(-) create mode 100644 build.rs create mode 100644 src/syscalls.rs diff --git a/Cargo.toml b/Cargo.toml index 8f53544..e435f66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,9 @@ categories = ["os::linux-apis"] [features] landlock = ["dep:landlock"] +aarch64 = ["syscalls/aarch64"] +x86_64 = ["syscalls/x86_64"] +riscv64 = ["syscalls/riscv64"] [dependencies] seccompiler = { version = "^0.4", default-features = false } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2c3f219 --- /dev/null +++ b/build.rs @@ -0,0 +1,12 @@ +fn main() { + // prefer method A if both method A and B are selected + if cfg!(feature = "__aarch64") || cfg!(target_arch = "aarch64") { + println!("cargo:rustc-cfg=enabled_arch=\"aarch64\""); + } else if cfg!(feature = "__riscv64") || cfg!(target_arch = "riscv64") { + println!("cargo:rustc-cfg=enabled_arch=\"riscv64\""); + } else if cfg!(feature = "__x86_64") || cfg!(target_arch = "x86_64") { + println!("cargo:rustc-cfg=enabled_arch=\"x86_64\""); + } else { + panic!("No architecture feature enabled and no supported architecture detected. Please enable one of the following features: x86_64, aarch64, riscv64"); + } +} diff --git a/src/builtins/basic.rs b/src/builtins/basic.rs index 6c6d0ca..829debf 100644 --- a/src/builtins/basic.rs +++ b/src/builtins/basic.rs @@ -1,7 +1,7 @@ //! Contains a [`RuleSet`] for allowing base syscalls that all programs will need, and are not //! dangerous for the most part. -use syscalls::Sysno; +use crate::syscalls::Sysno; use crate::RuleSet; @@ -39,7 +39,9 @@ impl RuleSet for BasicCapabilities { Sysno::set_robust_list, // Readlink isn't dangerous because you still need to be able to open the file to do // anything with the resolved name. + #[cfg(enabled_arch = "x86_64")] Sysno::readlink, + Sysno::readlinkat, // Getpid/tid is fine. Sysno::getpid, Sysno::gettid, diff --git a/src/builtins/danger_zone.rs b/src/builtins/danger_zone.rs index 44d53fe..bd06c57 100644 --- a/src/builtins/danger_zone.rs +++ b/src/builtins/danger_zone.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; -use syscalls::Sysno; +use crate::syscalls::Sysno; use crate::{RuleSet, SeccompRule}; @@ -91,7 +91,9 @@ pub struct ForkAndExec; impl RuleSet for ForkAndExec { fn simple_rules(&self) -> Vec { let mut rules = vec![ + #[cfg(enabled_arch = "x86_64")] Sysno::fork, + #[cfg(enabled_arch = "x86_64")] Sysno::vfork, Sysno::execve, Sysno::execveat, @@ -104,7 +106,11 @@ impl RuleSet for ForkAndExec { // musl creates a pipe when it starts a new process, and fails the operation if it can't // create the pipe if cfg!(target_env = "musl") { - rules.extend([Sysno::pipe, Sysno::pipe2]); + rules.extend([ + #[cfg(enabled_arch = "x86_64")] + Sysno::pipe, + Sysno::pipe2, + ]); } rules diff --git a/src/builtins/network.rs b/src/builtins/network.rs index 5c6bb09..868c238 100644 --- a/src/builtins/network.rs +++ b/src/builtins/network.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; -use syscalls::Sysno; +use crate::syscalls::Sysno; use super::YesReally; use crate::{RuleSet, SeccompRule}; @@ -11,19 +11,24 @@ use crate::{RuleSet, SeccompRule}; // TODO: add io_uring const NET_IO_SYSCALLS: &[Sysno] = &[ + #[cfg(enabled_arch = "x86_64")] Sysno::epoll_create, Sysno::epoll_create1, Sysno::epoll_ctl, + #[cfg(enabled_arch = "x86_64")] Sysno::epoll_wait, Sysno::epoll_pwait, Sysno::epoll_pwait2, + #[cfg(enabled_arch = "x86_64")] Sysno::select, Sysno::pselect6, + #[cfg(enabled_arch = "x86_64")] Sysno::poll, Sysno::ppoll, Sysno::accept, Sysno::accept4, // used in reqwest::blocking I guess to notify when blocking reads finish? + #[cfg(enabled_arch = "x86_64")] Sysno::eventfd, Sysno::eventfd2, // Used to set tcp_nodelay @@ -313,11 +318,11 @@ impl Networking { } impl RuleSet for Networking { - fn simple_rules(&self) -> Vec { + fn simple_rules(&self) -> Vec { self.allowed.iter().copied().collect() } - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { self.custom.clone() } diff --git a/src/builtins/pipes.rs b/src/builtins/pipes.rs index 679bb59..6a7796d 100644 --- a/src/builtins/pipes.rs +++ b/src/builtins/pipes.rs @@ -1,14 +1,18 @@ //! Contains a [`RuleSet`] for allowing pipes use crate::RuleSet; -use syscalls::Sysno; +use crate::syscalls::Sysno; /// [`Pipes`] allows you to create anonymous pipes for inter-process communication via the `pipe` /// syscalls. pub struct Pipes; impl RuleSet for Pipes { fn simple_rules(&self) -> Vec { - vec![Sysno::pipe, Sysno::pipe2] + vec![ + #[cfg(enabled_arch = "x86_64")] + Sysno::pipe, + Sysno::pipe2 + ] } fn name(&self) -> &'static str { diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index 79354aa..c9b629d 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd; #[cfg(feature = "landlock")] use std::path::{Path, PathBuf}; -use syscalls::Sysno; +use crate::syscalls::Sysno; #[cfg(feature = "landlock")] use crate::landlock::{access, AccessFs, BitFlags}; @@ -35,21 +35,36 @@ pub(crate) const IO_WRITE_SYSCALLS: &[Sysno] = &[ Sysno::fdatasync, Sysno::lseek, ]; -pub(crate) const IO_OPEN_SYSCALLS: &[Sysno] = &[Sysno::open, Sysno::openat, Sysno::openat2]; +pub(crate) const IO_OPEN_SYSCALLS: &[Sysno] = &[ + #[cfg(enabled_arch = "x86_64")] + Sysno::open, + Sysno::openat, + Sysno::openat2 +]; pub(crate) const IO_IOCTL_SYSCALLS: &[Sysno] = &[Sysno::ioctl, Sysno::fcntl]; // TODO: may want to separate fd-based and filename-based? pub(crate) const IO_METADATA_SYSCALLS: &[Sysno] = &[ + #[cfg(enabled_arch = "x86_64")] Sysno::stat, Sysno::fstat, + #[cfg(enabled_arch = "x86_64")] Sysno::newfstatat, + #[cfg(any(enabled_arch = "aarch64", enabled_arch = "riscv64"))] + Sysno::fstatat, + #[cfg(enabled_arch = "x86_64")] Sysno::lstat, Sysno::statx, + #[cfg(enabled_arch = "x86_64")] Sysno::getdents, Sysno::getdents64, Sysno::getcwd, ]; pub(crate) const IO_CLOSE_SYSCALLS: &[Sysno] = &[Sysno::close, Sysno::close_range]; -pub(crate) const IO_UNLINK_SYSCALLS: &[Sysno] = &[Sysno::unlink, Sysno::unlinkat]; +pub(crate) const IO_UNLINK_SYSCALLS: &[Sysno] = &[ + #[cfg(enabled_arch = "x86_64")] + Sysno::unlink, + Sysno::unlinkat +]; // TODO: split into SystemIO, SystemIOLandlock, SystemIOSeccompRestricted so that you can't call a // landlock function after using a seccomp argument filter function (or vice versa). You can still @@ -147,12 +162,15 @@ impl SystemIO { const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL; // | O_TMPFILE; // flags are the second argument for open but the third for openat - let rule = SeccompRule::new(Sysno::open) - .and_condition(seccomp_arg_filter!(arg1 & WRITECREATE == 0)); - self.custom - .entry(Sysno::open) - .or_insert_with(Vec::new) - .push(rule); + #[cfg(enabled_arch = "x86_64")] + { + let rule = SeccompRule::new(Sysno::open) + .and_condition(seccomp_arg_filter!(arg1 & WRITECREATE == 0)); + self.custom + .entry(Sysno::open) + .or_insert_with(Vec::new) + .push(rule); + } let rule = SeccompRule::new(Sysno::openat) .and_condition(seccomp_arg_filter!(arg2 & WRITECREATE == 0)); @@ -273,11 +291,11 @@ impl SystemIO { } impl RuleSet for SystemIO { - fn simple_rules(&self) -> Vec { + fn simple_rules(&self) -> Vec { self.allowed.iter().copied().collect() } - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { self.custom.clone() } diff --git a/src/builtins/time.rs b/src/builtins/time.rs index 472c960..81927ef 100644 --- a/src/builtins/time.rs +++ b/src/builtins/time.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; -use syscalls::Sysno; +use crate::syscalls::Sysno; use crate::RuleSet; diff --git a/src/error.rs b/src/error.rs index 79e5944..2c55120 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,7 @@ use landlock::RulesetError as LandlockError; pub enum ExtraSafeError { /// Error created when a simple Seccomp rule would override a conditional rule, or when trying to add a /// conditional rule when there's already a simple rule with the same syscall. - ConditionalNoEffectError(syscalls::Sysno, &'static str, &'static str), + ConditionalNoEffectError(crate::syscalls::Sysno, &'static str, &'static str), /// An error from the underlying seccomp library. SeccompError(SeccompilerError), /// No rules were enabled in the SafetyContext. diff --git a/src/lib.rs b/src/lib.rs index 849f47b..95190ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ pub use seccompiler::SeccompRule as SeccompilerRule; use seccompiler::SeccompAction; -pub use syscalls; +pub mod syscalls; pub mod error; pub use error::*; @@ -202,11 +202,11 @@ struct LabeledSeccompRule(pub &'static str, pub SeccompRule); /// functionality, such as opening files or starting threads. pub trait RuleSet { /// A simple rule is a seccomp rule that just allows the syscall without restriction. - fn simple_rules(&self) -> Vec; + fn simple_rules(&self) -> Vec; /// A conditional rule is a seccomp rule that uses a condition to restrict the syscall, e.g. only /// specific flags as parameters. - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { HashMap::new() } @@ -555,8 +555,8 @@ impl SafetyContext { ); } - #[cfg(not(all(target_arch = "x86_64", target_os = "linux")))] - compile_error!("extrasafe is currently only supported on linux x86_64"); + #[cfg(not(target_os = "linux"))] + compile_error!("extrasafe is currently only supported on linux"); let seccompiler_filter = SeccompilerFilter::new( rules_map, diff --git a/src/syscalls.rs b/src/syscalls.rs new file mode 100644 index 0000000..e8e6a71 --- /dev/null +++ b/src/syscalls.rs @@ -0,0 +1,25 @@ +//! Syscalls export +//! This module re-exports syscalls for the target architecture. + +#[cfg(all(feature = "x86_64", feature = "__aarch64"))] +compile_error!("feature \"x86_64\" and feature \"aarch64\" cannot be enabled at the same time!"); +#[cfg(all(feature = "x86_64", feature = "__riscv64"))] +compile_error!("feature \"x86_64\" and feature \"riscv64\" cannot be enabled at the same time!"); +#[cfg(all(feature = "__riscv64", feature = "__aarch64"))] +compile_error!("feature \"riscv64\" and feature \"aarch64\" cannot be enabled at the same time!"); + +#[cfg(all( + any(feature = "x86_64", target_arch = "x86_64"), + not(any(feature = "aarch64", feature = "riscv64")) +))] +pub use syscalls::x86_64::*; +#[cfg(all( + any(feature = "aarch64", target_arch = "aarch64"), + not(any(feature = "x86_64", feature = "riscv64")) +))] +pub use syscalls::aarch64::*; +#[cfg(all( + any(feature = "riscv64", target_arch = "riscv64"), + not(any(feature = "x86_64", feature = "aarch64")) +))] +pub use syscalls::riscv64::*; diff --git a/tests/arg_comparisons.rs b/tests/arg_comparisons.rs index 30db494..cfff972 100644 --- a/tests/arg_comparisons.rs +++ b/tests/arg_comparisons.rs @@ -3,7 +3,7 @@ use builtins::SystemIO; use extrasafe::*; -use syscalls::Sysno; +use extrasafe::syscalls::Sysno; use std::collections::HashMap; diff --git a/tests/sysno.rs b/tests/sysno.rs index 78d6a86..3005f58 100644 --- a/tests/sysno.rs +++ b/tests/sysno.rs @@ -1,5 +1,5 @@ use extrasafe::SafetyContext; -use syscalls::Sysno; +use extrasafe::syscalls::Sysno; #[test] #[should_panic] From 6521a6cb6a3c75dfd2b63206e5730f7fd8c1eef9 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 17 Mar 2024 12:38:37 +0100 Subject: [PATCH 03/25] Fix Makefile --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 99b2bd9..02d7d3f 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: - cargo build --all-features + cargo build --feature=landlock # Run all tests and examples test: @@ -7,12 +7,12 @@ test: # Run all tests with and without all features test-ci: - cargo test --target=$(TARGET_TRIPLE) --tests --examples --all-features + cargo test --target=$(TARGET_TRIPLE) --tests --examples --feature=landlock cargo test --target=$(TARGET_TRIPLE) --tests --examples --no-default-features # Run clippy lint: - cargo clippy --no-deps --all-targets --all-features -- -W clippy::pedantic \ + cargo clippy --no-deps --all-targets --feature=landlock -- -W clippy::pedantic \ -A clippy::let-unit-value \ -A clippy::wildcard-imports \ -A clippy::module-name-repetitions \ @@ -24,8 +24,8 @@ doc: # Compute test coverage for CI with llvm-cov coverage-ci: - cargo llvm-cov --tests --examples --all-targets --all-features --workspace --lcov --output-path lcov.info + cargo llvm-cov --tests --examples --all-targets --feature=landlock --workspace --lcov --output-path lcov.info # Compute test coverage with HTML output coverage: - cargo llvm-cov --tests --examples --all-targets --all-features --workspace --html + cargo llvm-cov --tests --examples --all-targets --feature=landlock --workspace --html From 379f4cfbedcc0b65c3ea27cf7589017d0b235810 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 17 Mar 2024 12:39:55 +0100 Subject: [PATCH 04/25] Ensure builds work --- .github/workflows/build-test.yaml | 24 ++++++++++++++++++++++++ Makefile | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index a5e92eb..e45305f 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,6 +36,30 @@ jobs: - name: Run tests run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} + check: + name: Check if code compiles + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target-triple: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + - riscv64gc-unknown-linux-gnu + - riscv64gc-unknown-linux-musl + steps: + - name: Install musl lib + if: ${{ contains(matrix.target-triple, 'musl') }} + run: sudo apt-get install musl-tools + - name: Install target triple + run: rustup target install ${{ matrix.target-triple }} + - name: Checkout + uses: actions/checkout@v4 + - name: Run tests + run: make check-ci TARGET_TRIPLE=${{ matrix.target-triple }} + doc: name: Documentation Check runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index 02d7d3f..4e1643d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,14 @@ build: cargo build --feature=landlock +# Checks if code can be built +check: + cargo check --features=landlock + +check-ci: + cargo check --target=$(TARGET_TRIPLE) --features=landlock + cargo check --target=$(TARGET_TRIPLE) --no-default-features + # Run all tests and examples test: cargo test --tests --examples --all-features From f1318b151577b3d019a0ecd8abfc4d427c403233 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 17 Mar 2024 13:02:51 +0100 Subject: [PATCH 05/25] Revert "cargo fmt, fix some warnings" This reverts commit 9de68eaaa04c8a02ac5d7d6c3c854e591dc8b897. --- examples/ipc_server_with_database.rs | 110 +++++++------ examples/network_server.rs | 14 +- examples/no_files_allow_stdout.rs | 2 +- examples/server_with_database.rs | 101 +++++------- examples/simple_network.rs | 45 +++--- examples/time.rs | 20 ++- examples/user-guide.rs | 40 ++--- src/builtins/basic.rs | 16 +- src/builtins/danger_zone.rs | 11 +- src/builtins/mod.rs | 4 +- src/builtins/network.rs | 68 +++----- src/builtins/pipes.rs | 4 +- src/builtins/systemio.rs | 94 +++++------ src/builtins/time.rs | 12 +- src/error.rs | 4 +- src/landlock.rs | 10 +- src/lib.rs | 90 ++++------- src/macros.rs | 76 ++------- tests/arg_comparisons.rs | 44 +++--- tests/bad_combination.rs | 77 +++++---- tests/default_deny.rs | 9 +- tests/inherit_filters.rs | 5 +- tests/landlock_allthreads_fail.rs | 16 +- tests/landlock_basic.rs | 223 +++++++++------------------ tests/landlock_conflicts.rs | 86 +++++------ tests/multiple_conditions.rs | 25 ++- tests/multiple_filters.rs | 37 +++-- tests/network.rs | 88 +++++------ tests/ruleset_union.rs | 63 ++++---- tests/sleep.rs | 22 +-- tests/test_ref_ruleset.rs | 6 +- tests/tests_can_fail.rs | 3 +- tests/thread_multi.rs | 8 +- tests/thread_single.rs | 9 +- tests/unsupported_os.rs | 3 +- 35 files changed, 607 insertions(+), 838 deletions(-) diff --git a/examples/ipc_server_with_database.rs b/examples/ipc_server_with_database.rs index f3c1a77..1292f2f 100644 --- a/examples/ipc_server_with_database.rs +++ b/examples/ipc_server_with_database.rs @@ -36,10 +36,7 @@ type DbConn = Arc>; fn run_subprocess(cmd: &[&str]) -> std::process::Child { let exe_path = std::env::current_exe().unwrap(); - let args: Vec<_> = ["run_main", "--", "--sub"] - .iter() - .chain(cmd.iter()) - .collect(); + let args: Vec<_> = ["run_main", "--", "--sub"].iter().chain(cmd.iter()).collect(); std::process::Command::new(exe_path.to_str().unwrap()) .arg0(format!("{}-subprocess", cmd[0])) @@ -66,14 +63,13 @@ fn run_webserver(db_socket_path: &str) { // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build() - .unwrap(); + .build().unwrap(); let listener = std::net::TcpListener::bind("127.0.0.1:5576").unwrap(); // extrasafe context SafetyContext::new() - .enable(Networking::nothing().allow_running_tcp_servers()) - .unwrap() + .enable(Networking::nothing() + .allow_running_tcp_servers()).unwrap() .apply_to_current_thread() .unwrap(); @@ -115,7 +111,8 @@ fn run_webserver(db_socket_path: &str) { .to_string(); messages - })); + }) + ); let svc = warp::service(routes); let make_svc = hyper::service::make_service_fn(move |_| { @@ -150,31 +147,25 @@ fn run_db(socket_path: &str) { db.pragma_update(None, "locking_mode", "exclusive").unwrap(); db.pragma_update(None, "journal_mode", "wal").unwrap(); - db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []) - .unwrap(); + db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []).unwrap(); let mut get_rows = db.prepare("SELECT msg FROM messages;").unwrap(); let mut insert_row = db.prepare("INSERT INTO messages VALUES (?)").unwrap(); // after opening connection socket and db file, set extrasafe context SafetyContext::new() - .enable( - Networking::nothing() - .allow_connect() - .yes_really() - .allow_running_unix_servers(), - ) - .unwrap() - .enable( - SystemIO::nothing() - .allow_read() - .allow_write() - .allow_metadata() - .allow_ioctl() - .allow_close(), - ) - .unwrap() - .enable(Threads::nothing().allow_sleep().yes_really()) - .unwrap() + .enable(Networking::nothing() + .allow_connect() + .yes_really() + .allow_running_unix_servers() + ).unwrap() + .enable(SystemIO::nothing() + .allow_read() + .allow_write() + .allow_metadata() + .allow_ioctl() + .allow_close()).unwrap() + .enable(Threads::nothing() + .allow_sleep().yes_really()).unwrap() .apply_to_current_thread() .unwrap(); @@ -206,17 +197,18 @@ fn run_db(socket_path: &str) { let msg: DBMsg; if buf == "list" { msg = DBMsg::List; - } else if buf.starts_with("write") { + } + else if buf.starts_with("write") { msg = DBMsg::Write(buf[6..].to_string()); - } else { + } + else { panic!("unknown message recieved in db: {}", buf); } match msg { DBMsg::List => { let messages: Vec = get_rows - .query_map([], |row| row.get(0)) - .unwrap() + .query_map([], |row| row.get(0)).unwrap() .map(Result::unwrap) .collect(); @@ -239,8 +231,8 @@ fn run_client_write(msg: &str) { // Set up extrasafe context SafetyContext::new() - .enable(Networking::nothing().allow_start_tcp_clients()) - .unwrap() + .enable(Networking::nothing() + .allow_start_tcp_clients()).unwrap() .apply_to_current_thread() .unwrap(); println!("about to make request with msg {}", msg); @@ -279,35 +271,34 @@ fn run_client_read() { // enable extrasafe context let ctx = SafetyContext::new() - .enable( - Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers() - .yes_really() - .allow_start_tcp_clients(), - ) - .unwrap() + .enable(Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers().yes_really() + .allow_start_tcp_clients() + ).unwrap() // For some reason only if we make two requests with a client does it use multiple threads, // so we only need them in the reader thread rather than the writer. - .enable(Threads::nothing().allow_create()) - .unwrap(); + .enable(Threads::nothing() + .allow_create()).unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx - .enable( + let ctx = ctx.enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ) - .unwrap(); + ).unwrap(); #[cfg(feature = "landlock")] - let ctx = ctx - .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) + let ctx = ctx.enable( + SystemIO::nothing() + .allow_dns_files() + .allow_ssl_files() + ).unwrap(); + + ctx.apply_to_current_thread() .unwrap(); - ctx.apply_to_current_thread().unwrap(); // make request runtime.block_on(async { @@ -342,13 +333,16 @@ fn main() { if args.contains(&"--sub".into()) { // If args is "example_prog [possible other options] --sub subcommand subargs...", run the subcommand if let Some(idx) = args.iter().position(|s| s == "db") { - run_db(&args[idx + 1]); - } else if let Some(idx) = args.iter().position(|s| s == "webserver") { - run_webserver(&args[idx + 1]); - } else if args.contains(&"read_client".into()) { + run_db(&args[idx+1]); + } + else if let Some(idx) = args.iter().position(|s| s == "webserver") { + run_webserver(&args[idx+1]); + } + else if args.contains(&"read_client".into()) { run_client_read(); - } else if let Some(idx) = args.iter().position(|s| s == "write_client") { - run_client_write(&args[idx + 1]); + } + else if let Some(idx) = args.iter().position(|s| s == "write_client") { + run_client_write(&args[idx+1]); } return; } diff --git a/examples/network_server.rs b/examples/network_server.rs index 8533f02..cba3b6d 100644 --- a/examples/network_server.rs +++ b/examples/network_server.rs @@ -87,14 +87,12 @@ fn main() { // we can enable safetycontext, rather than just waiting 50ms. thread::sleep(std::time::Duration::from_millis(50)); SafetyContext::new() - .enable( - Networking::nothing() - .allow_running_tcp_servers() - .allow_start_tcp_clients(), - ) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .enable(Networking::nothing() + .allow_running_tcp_servers() + .allow_start_tcp_clients() + ).unwrap() + .enable(Threads::nothing() + .allow_create()).unwrap() .apply_to_all_threads() .unwrap(); diff --git a/examples/no_files_allow_stdout.rs b/examples/no_files_allow_stdout.rs index d8ee6e7..e85d4e9 100644 --- a/examples/no_files_allow_stdout.rs +++ b/examples/no_files_allow_stdout.rs @@ -11,7 +11,7 @@ fn main() { .enable( extrasafe::builtins::SystemIO::nothing() .allow_stdout() - .allow_stderr(), + .allow_stderr() ) .unwrap() .apply_to_all_threads(); diff --git a/examples/server_with_database.rs b/examples/server_with_database.rs index 5f9fcba..77d4c89 100644 --- a/examples/server_with_database.rs +++ b/examples/server_with_database.rs @@ -61,20 +61,18 @@ fn run_server() { // spawn db server thread std::thread::Builder::new() .name("db".into()) - .spawn(move || run_db(&db_queue)) - .unwrap(); - + .spawn(move || run_db(&db_queue)).unwrap(); + // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build() - .unwrap(); + .build().unwrap(); let listener = std::net::TcpListener::bind("127.0.0.1:5575").unwrap(); // extrasafe context SafetyContext::new() - .enable(Networking::nothing().allow_running_tcp_servers()) - .unwrap() + .enable(Networking::nothing() + .allow_running_tcp_servers()).unwrap() .apply_to_current_thread() .unwrap(); @@ -100,7 +98,8 @@ fn run_server() { let messages = recv.recv().unwrap(); messages.join("\n") - })); + }) + ); let svc = warp::service(routes); let make_svc = hyper::service::make_service_fn(move |_| { @@ -131,24 +130,20 @@ fn run_db(queue: &DbConn) { db.pragma_update(None, "locking_mode", "exclusive").unwrap(); db.pragma_update(None, "journal_mode", "wal").unwrap(); - db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []) - .unwrap(); + db.execute("CREATE TABLE messages ( msg TEXT NOT NULL );", []).unwrap(); let mut get_rows = db.prepare("SELECT msg FROM messages;").unwrap(); let mut insert_row = db.prepare("INSERT INTO messages VALUES (?)").unwrap(); // after opening file, set extrasafe context SafetyContext::new() - .enable( - SystemIO::nothing() - .allow_read() - .allow_write() - .allow_metadata() - .allow_ioctl() - .allow_close(), - ) - .unwrap() - .enable(Threads::nothing().allow_sleep().yes_really()) - .unwrap() + .enable(SystemIO::nothing() + .allow_read() + .allow_write() + .allow_metadata() + .allow_ioctl() + .allow_close()).unwrap() + .enable(Threads::nothing() + .allow_sleep().yes_really()).unwrap() .apply_to_current_thread() .unwrap(); @@ -167,8 +162,7 @@ fn run_db(queue: &DbConn) { match msg { DBMsg::List(send) => { let messages: Vec = get_rows - .query_map([], |row| row.get(0)) - .unwrap() + .query_map([], |row| row.get(0)).unwrap() .map(Result::unwrap) .collect(); @@ -185,13 +179,12 @@ fn run_client_write(msg: &str) { // set up runtime let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() - .build() - .unwrap(); + .build().unwrap(); // Set up extrasafe context SafetyContext::new() - .enable(Networking::nothing().allow_start_tcp_clients()) - .unwrap() + .enable(Networking::nothing() + .allow_start_tcp_clients()).unwrap() .apply_to_current_thread() .unwrap(); println!("about to make request with msg {}", msg); @@ -223,42 +216,39 @@ fn run_client_read() { let runtime = tokio::runtime::Builder::new_current_thread() .worker_threads(1) .enable_all() - .build() - .unwrap(); + .build().unwrap(); let client = reqwest::Client::new(); // enable extrasafe context let ctx = SafetyContext::new() - .enable( - Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers() - .yes_really() - .allow_start_tcp_clients(), - ) - .unwrap() + .enable(Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers().yes_really() + .allow_start_tcp_clients() + ).unwrap() // For some reason only if we make two requests with a client does it use multiple threads, // so we only need them in the reader thread rather than the writer. - .enable(Threads::nothing().allow_create()) - .unwrap(); + .enable(Threads::nothing() + .allow_create()).unwrap(); #[cfg(feature = "landlock")] - let ctx = ctx - .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) - .unwrap(); + let ctx = ctx.enable( + SystemIO::nothing() + .allow_dns_files() + .allow_ssl_files() + ).unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx - .enable( + let ctx = ctx.enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ) - .unwrap(); + ).unwrap(); - ctx.apply_to_current_thread().unwrap(); + ctx.apply_to_current_thread() + .unwrap(); // make request runtime.block_on(async { @@ -272,10 +262,7 @@ fn run_client_read() { res.unwrap_err() ); let text = res.unwrap(); - println!( - "first 10 bytes of response from example.org {}", - &text[..10] - ); + println!("first 10 bytes of response from example.org {}", &text[..10]); let res = client.get("http://127.0.0.1:5575/read").send().await; assert!( @@ -294,8 +281,7 @@ fn main() { // -- Spawn server let _server_thread = std::thread::Builder::new() .name("server".into()) - .spawn(run_server) - .unwrap(); + .spawn(run_server).unwrap(); // give server time to start up std::thread::sleep(std::time::Duration::from_millis(100)); @@ -303,8 +289,7 @@ fn main() { // -- write "hello" to db let client1_thread = std::thread::Builder::new() .name("client1".into()) - .spawn(|| run_client_write("hello")) - .unwrap(); + .spawn(|| run_client_write("hello")).unwrap(); let res1 = client1_thread.join(); assert!(res1.is_ok(), "client1 failed: {:?}", res1.unwrap_err()); @@ -312,8 +297,7 @@ fn main() { // -- write "extrasafe" to db let client2_thread = std::thread::Builder::new() .name("client2".into()) - .spawn(|| run_client_write("extrasafe")) - .unwrap(); + .spawn(|| run_client_write("extrasafe")).unwrap(); let res2 = client2_thread.join(); assert!(res2.is_ok(), "client2 failed: {:?}", res2.unwrap_err()); @@ -321,8 +305,7 @@ fn main() { // -- read back, check messages are there in order let client3_thread = std::thread::Builder::new() .name("client3".into()) - .spawn(run_client_read) - .unwrap(); + .spawn(run_client_read).unwrap(); let res3 = client3_thread.join(); assert!(res3.is_ok(), "client3 failed: {:?}", res3.unwrap_err()); } diff --git a/examples/simple_network.rs b/examples/simple_network.rs index a6eedfa..7d5d7ae 100644 --- a/examples/simple_network.rs +++ b/examples/simple_network.rs @@ -5,53 +5,51 @@ //! the root certificate store is included in the binary. See the `Cargo.toml` configuration for //! musl. -use extrasafe::builtins::{danger_zone::Threads, Networking, SystemIO}; use extrasafe::*; +use extrasafe::builtins::{danger_zone::Threads, Networking, SystemIO}; fn main() { // do as much setup before enabling extrasafe so we can enable the least amount of syscalls let runtime = tokio::runtime::Builder::new_current_thread() .worker_threads(1) .enable_all() - .build() - .unwrap(); + .build().unwrap(); let client = reqwest::Client::new(); let ctx = SafetyContext::new() - .enable( - Networking::nothing() - // Necessary for DNS - .allow_start_udp_servers() - .yes_really() - .allow_start_tcp_clients(), - ) - .unwrap() + .enable(Networking::nothing() + // Necessary for DNS + .allow_start_udp_servers().yes_really() + .allow_start_tcp_clients() + ).unwrap() // hyper (via reqwest) seems to want to spawn a separate blocking thread to do DNS and it // doesn't seem like it can be preallocated easily. // TODO: investigate using runtime::Builder::thread_keep_alive and max_blocking_threads to // effectively preallocate by then just doing `block_on(|| ())` - .enable(Threads::nothing().allow_create()) - .unwrap(); + .enable(Threads::nothing() + .allow_create() + ).unwrap(); // allow access to dns and ssl files // note that allowing ssl file access isn't necessary if using rustls with webpki-certs // and allowing the dns files isn't strictly necessary either depending on various system // configurations #[cfg(feature = "landlock")] - let ctx = ctx - .enable(SystemIO::nothing().allow_dns_files().allow_ssl_files()) - .unwrap(); + let ctx = ctx.enable( + SystemIO::nothing() + .allow_dns_files() + .allow_ssl_files() + ).unwrap(); #[cfg(not(feature = "landlock"))] - let ctx = ctx - .enable( + let ctx = ctx.enable( SystemIO::nothing() .allow_open_readonly() .allow_read() .allow_metadata() .allow_close(), - ) - .unwrap(); + ).unwrap(); - ctx.apply_to_current_thread().unwrap(); + ctx.apply_to_current_thread() + .unwrap(); // make an http request runtime.block_on(async { @@ -65,10 +63,7 @@ fn main() { res.unwrap_err() ); let text = res.unwrap(); - println!( - "first 10 bytes of response from example.org {}", - &text[..10] - ); + println!("first 10 bytes of response from example.org {}", &text[..10]); }); } diff --git a/examples/time.rs b/examples/time.rs index 256792d..7abf07b 100644 --- a/examples/time.rs +++ b/examples/time.rs @@ -1,16 +1,20 @@ -use builtins::{SystemIO, Time}; -use extrasafe::{builtins, SafetyContext}; +use extrasafe::{SafetyContext, builtins}; +use builtins::{Time, SystemIO}; fn main() { SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout()) - .unwrap() + .enable( + SystemIO::nothing() + .allow_stdout() + ).unwrap() + // On most systems this won't have an effect because glibc and musl both use vDSOs that // compute time directly via rdtsc rather than calling the syscalls directly. - .enable(Time::nothing().allow_gettime()) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + Time::nothing() + .allow_gettime() + ).unwrap() + .apply_to_current_thread().unwrap(); let time = std::time::SystemTime::now(); println!("time gave us: {:#?}", time); diff --git a/examples/user-guide.rs b/examples/user-guide.rs index 0ff4f9e..2937d88 100644 --- a/examples/user-guide.rs +++ b/examples/user-guide.rs @@ -1,19 +1,20 @@ fn simple_example() { - use extrasafe::builtins::{Networking, SystemIO}; + use extrasafe::builtins::{SystemIO, Networking}; let ctx = extrasafe::SafetyContext::new(); let ctx = ctx - .enable(SystemIO::nothing().allow_open_readonly()) - .expect("Failed to add systemio ruleset to context") + .enable( + SystemIO::nothing() + .allow_open_readonly() + ).expect("Failed to add systemio ruleset to context") // The Networking RuleSet includes both read and write, but our files will be opened // readonly so we can't actually write to them. We can still write to stdout and stderr // though. .enable( Networking::nothing() .allow_start_tcp_clients() - .allow_running_tcp_clients(), - ) - .expect("Failed to add networking ruleset to context"); + .allow_running_tcp_clients() + ).expect("Failed to add networking ruleset to context"); ctx.apply_to_current_thread() .expect("Failed to apply seccomp filters"); @@ -21,8 +22,8 @@ fn simple_example() { } fn custom_ruleset() { - use extrasafe::syscalls::Sysno; use extrasafe::*; + use extrasafe::syscalls::Sysno; use std::collections::HashMap; @@ -39,8 +40,11 @@ fn custom_ruleset() { const SOCK_STREAM: u64 = libc::SOCK_STREAM as u64; let rule = SeccompRule::new(Sysno::socket) - .and_condition(seccomp_arg_filter!(arg0 & SOCK_STREAM == SOCK_STREAM)); - HashMap::from([(Sysno::socket, vec![rule])]) + .and_condition( + seccomp_arg_filter!(arg0 & SOCK_STREAM == SOCK_STREAM)); + HashMap::from([ + (Sysno::socket, vec![rule,]) + ]) } fn name(&self) -> &'static str { @@ -49,10 +53,8 @@ fn custom_ruleset() { } extrasafe::SafetyContext::new() - .enable(MyRuleSet) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(MyRuleSet).unwrap() + .apply_to_current_thread().unwrap(); } #[cfg(feature = "landlock")] @@ -63,13 +65,11 @@ fn with_landlock() { extrasafe::SafetyContext::new() .enable( - extrasafe::builtins::SystemIO::nothing() - .allow_create_in_dir(&tmp_dir_allow) - .allow_write_file(&tmp_dir_allow), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + extrasafe::builtins::SystemIO::nothing() + .allow_create_in_dir(&tmp_dir_allow) + .allow_write_file(&tmp_dir_allow) + ).unwrap() + .apply_to_current_thread().unwrap(); // Opening arbitrary files now fails! let res = File::create(tmp_dir_deny.join("evil.txt")); diff --git a/src/builtins/basic.rs b/src/builtins/basic.rs index 829debf..f8b6b64 100644 --- a/src/builtins/basic.rs +++ b/src/builtins/basic.rs @@ -1,9 +1,9 @@ //! Contains a [`RuleSet`] for allowing base syscalls that all programs will need, and are not //! dangerous for the most part. -use crate::syscalls::Sysno; +use std::collections::HashMap; -use crate::RuleSet; +use crate::{SeccompRule, RuleSet, syscalls::Sysno}; /// A [`RuleSet`] allowing basic required syscalls to do things like allocate memory, and also a few that are used by /// Rust to set up panic handling and segfault handlers. @@ -24,6 +24,7 @@ impl RuleSet for BasicCapabilities { Sysno::mprotect, Sysno::munlock, Sysno::munlockall, + // Rust installs a signal handler to distinguish stack overflows from other faults // https://github.com/iximeow/rust/blob/master/src/libstd/sys/unix/stack_overflow.rs#L46 // (I learned this by getting a segfault when not allowing sigaction/etc and then @@ -33,30 +34,37 @@ impl RuleSet for BasicCapabilities { Sysno::rt_sigaction, Sysno::rt_sigprocmask, Sysno::rt_sigreturn, + // Futex management Sysno::futex, Sysno::get_robust_list, Sysno::set_robust_list, + // Readlink isn't dangerous because you still need to be able to open the file to do // anything with the resolved name. #[cfg(enabled_arch = "x86_64")] Sysno::readlink, Sysno::readlinkat, + // Getpid/tid is fine. Sysno::getpid, Sysno::gettid, + // Get kernel info Sysno::uname, + // Could maybe put in a separate ruleset Sysno::getrandom, + // Thread affinity and yield seems okay to put here but I could be convinced to put it // in the Multiprocessing ruleset. they probably should be there. - Sysno::sched_getaffinity, - Sysno::sched_setaffinity, + Sysno::sched_getaffinity, Sysno::sched_setaffinity, Sysno::sched_yield, + // rseq is used in newer glibc for some initialization purposes. // It's kind of complicated but does not appear to be dangerous. Sysno::rseq, + // Exiting is probably fine. Sysno::exit, Sysno::exit_group, diff --git a/src/builtins/danger_zone.rs b/src/builtins/danger_zone.rs index bd06c57..cdbb6ed 100644 --- a/src/builtins/danger_zone.rs +++ b/src/builtins/danger_zone.rs @@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet}; use crate::syscalls::Sysno; -use crate::{RuleSet, SeccompRule}; +use crate::{SeccompRule, RuleSet}; use super::YesReally; @@ -95,12 +95,9 @@ impl RuleSet for ForkAndExec { Sysno::fork, #[cfg(enabled_arch = "x86_64")] Sysno::vfork, - Sysno::execve, - Sysno::execveat, - Sysno::wait4, - Sysno::waitid, - Sysno::clone, - Sysno::clone3, + Sysno::execve, Sysno::execveat, + Sysno::wait4, Sysno::waitid, + Sysno::clone, Sysno::clone3, ]; // musl creates a pipe when it starts a new process, and fails the operation if it can't diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index c24ecf7..43b3c50 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -16,7 +16,9 @@ impl YesReally { /// Make a [`YesReally`]. pub fn new(inner: T) -> YesReally { - YesReally { inner } + YesReally { + inner, + } } } diff --git a/src/builtins/network.rs b/src/builtins/network.rs index 868c238..38edb0f 100644 --- a/src/builtins/network.rs +++ b/src/builtins/network.rs @@ -5,7 +5,7 @@ use std::collections::{HashMap, HashSet}; use crate::syscalls::Sysno; use super::YesReally; -use crate::{RuleSet, SeccompRule}; +use crate::{SeccompRule, RuleSet}; // TODO: make bind calls conditional on the DGRAM/UNIX/STREAM flag in each function @@ -13,55 +13,37 @@ use crate::{RuleSet, SeccompRule}; const NET_IO_SYSCALLS: &[Sysno] = &[ #[cfg(enabled_arch = "x86_64")] Sysno::epoll_create, - Sysno::epoll_create1, - Sysno::epoll_ctl, + Sysno::epoll_create1, Sysno::epoll_ctl, #[cfg(enabled_arch = "x86_64")] Sysno::epoll_wait, - Sysno::epoll_pwait, - Sysno::epoll_pwait2, + Sysno::epoll_pwait, Sysno::epoll_pwait2, #[cfg(enabled_arch = "x86_64")] Sysno::select, Sysno::pselect6, #[cfg(enabled_arch = "x86_64")] Sysno::poll, - Sysno::ppoll, - Sysno::accept, - Sysno::accept4, + Sysno::ppoll, Sysno::accept, Sysno::accept4, // used in reqwest::blocking I guess to notify when blocking reads finish? #[cfg(enabled_arch = "x86_64")] Sysno::eventfd, Sysno::eventfd2, // Used to set tcp_nodelay - Sysno::fcntl, - Sysno::ioctl, + Sysno::fcntl, Sysno::ioctl, Sysno::getsockopt, Sysno::setsockopt, + // Misc socket info Sysno::getpeername, Sysno::getsockname, ]; // listen is technically not a "read" syscall but you'd never listen and not read. -const NET_READ_SYSCALLS: &[Sysno] = &[ - Sysno::listen, - Sysno::recvfrom, - Sysno::recvmsg, - Sysno::recvmmsg, - Sysno::read, - Sysno::readv, - Sysno::preadv, - Sysno::preadv2, -]; -const NET_WRITE_SYSCALLS: &[Sysno] = &[ - Sysno::sendto, - Sysno::sendmsg, - Sysno::sendmmsg, - Sysno::sendfile, - Sysno::write, - Sysno::writev, - Sysno::pwritev, - Sysno::pwritev2, -]; +const NET_READ_SYSCALLS: &[Sysno] = &[Sysno::listen, + Sysno::recvfrom, Sysno::recvmsg, Sysno::recvmmsg, + Sysno::read, Sysno::readv, Sysno::preadv, Sysno::preadv2]; +const NET_WRITE_SYSCALLS: &[Sysno] = &[Sysno::sendto, Sysno::sendmsg, Sysno::sendmmsg, + Sysno::sendfile, + Sysno::write, Sysno::writev, Sysno::pwritev, Sysno::pwritev2]; // TODO: refactor Socket rule creation to reduce duplication in the allow_start_*_server functions @@ -129,16 +111,14 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); @@ -177,16 +157,14 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); @@ -223,19 +201,17 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET == AF_INET)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // IPv6 let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_INET6 == AF_INET6)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); - + self.allowed.extend(&[Sysno::connect]); self.allowed.extend(NET_IO_SYSCALLS); self.allowed.extend(NET_READ_SYSCALLS); @@ -272,16 +248,14 @@ impl Networking { let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_UNIX == AF_UNIX)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_STREAM == SOCK_STREAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); // DGRAM let rule = SeccompRule::new(Sysno::socket) .and_condition(seccomp_arg_filter!(arg0 & AF_UNIX == AF_UNIX)) .and_condition(seccomp_arg_filter!(arg1 & SOCK_DGRAM == SOCK_DGRAM)); - self.custom - .entry(Sysno::socket) + self.custom.entry(Sysno::socket) .or_insert_with(Vec::new) .push(rule); diff --git a/src/builtins/pipes.rs b/src/builtins/pipes.rs index 6a7796d..72ca53e 100644 --- a/src/builtins/pipes.rs +++ b/src/builtins/pipes.rs @@ -1,7 +1,7 @@ //! Contains a [`RuleSet`] for allowing pipes -use crate::RuleSet; -use crate::syscalls::Sysno; +use std::collections::HashMap; +use crate::{SeccompRule, RuleSet, syscalls::Sysno}; /// [`Pipes`] allows you to create anonymous pipes for inter-process communication via the `pipe` /// syscalls. diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index c9b629d..384a2c3 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -1,6 +1,6 @@ //! Contains a [`RuleSet`] for allowing IO-related syscalls, like file opening, reading, and writing. -use std::collections::{HashMap, HashSet}; +use std::collections::{HashSet, HashMap}; use std::fs::File; use std::os::unix::io::AsRawFd; @@ -9,13 +9,13 @@ use std::path::{Path, PathBuf}; use crate::syscalls::Sysno; -#[cfg(feature = "landlock")] -use crate::landlock::{access, AccessFs, BitFlags}; #[cfg(feature = "landlock")] use crate::LandlockRule; +#[cfg(feature = "landlock")] +use crate::landlock::{access, AccessFs, BitFlags}; -use super::YesReally; use crate::{RuleSet, SeccompRule}; +use super::YesReally; pub(crate) const IO_READ_SYSCALLS: &[Sysno] = &[ Sysno::read, @@ -92,7 +92,7 @@ impl SystemIO { allowed: HashSet::new(), custom: HashMap::new(), #[cfg(feature = "landlock")] - landlock_rules: HashMap::new(), + landlock_rules: HashMap::new() } } @@ -101,8 +101,7 @@ impl SystemIO { SystemIO::nothing() .allow_read() .allow_write() - .allow_open() - .yes_really() + .allow_open().yes_really() .allow_metadata() .allow_unlink() .allow_close() @@ -159,23 +158,21 @@ impl SystemIO { // WRONLY or RDWR is required for O_TMPFILE so we're fine to leave it out anyway. // const O_TMPFILE: u64 = libc::O_TMPFILE as u64; - const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL; // | O_TMPFILE; + const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL;// | O_TMPFILE; // flags are the second argument for open but the third for openat #[cfg(enabled_arch = "x86_64")] { let rule = SeccompRule::new(Sysno::open) .and_condition(seccomp_arg_filter!(arg1 & WRITECREATE == 0)); - self.custom - .entry(Sysno::open) + self.custom.entry(Sysno::open) .or_insert_with(Vec::new) .push(rule); } let rule = SeccompRule::new(Sysno::openat) .and_condition(seccomp_arg_filter!(arg2 & WRITECREATE == 0)); - self.custom - .entry(Sysno::openat) + self.custom.entry(Sysno::openat) .or_insert_with(Vec::new) .push(rule); @@ -205,9 +202,9 @@ impl SystemIO { /// Allow reading from stdin pub fn allow_stdin(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::read).and_condition(seccomp_arg_filter!(arg0 == 0)); - self.custom - .entry(Sysno::read) + let rule = SeccompRule::new(Sysno::read) + .and_condition(seccomp_arg_filter!(arg0 == 0)); + self.custom.entry(Sysno::read) .or_insert_with(Vec::new) .push(rule); @@ -216,9 +213,9 @@ impl SystemIO { /// Allow writing to stdout pub fn allow_stdout(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == 1)); - self.custom - .entry(Sysno::write) + let rule = SeccompRule::new(Sysno::write) + .and_condition(seccomp_arg_filter!(arg0 == 1)); + self.custom.entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -227,9 +224,9 @@ impl SystemIO { /// Allow writing to stderr pub fn allow_stderr(mut self) -> SystemIO { - let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == 2)); - self.custom - .entry(Sysno::write) + let rule = SeccompRule::new(Sysno::write) + .and_condition(seccomp_arg_filter!(arg0 == 2)); + self.custom.entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -245,21 +242,18 @@ impl SystemIO { /// it's possible that the fd will be reused and therefore may be read from. #[allow(clippy::missing_panics_doc)] pub fn allow_file_read(mut self, file: &File) -> SystemIO { - let fd = file - .as_raw_fd() - .try_into() - .expect("provided fd was negative"); + let fd = file.as_raw_fd().try_into().expect("provided fd was negative"); for &syscall in IO_READ_SYSCALLS { - let rule = SeccompRule::new(syscall).and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom - .entry(syscall) + let rule = SeccompRule::new(syscall) + .and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom.entry(syscall) .or_insert_with(Vec::new) .push(rule); } for &syscall in IO_METADATA_SYSCALLS { - let rule = SeccompRule::new(syscall).and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom - .entry(syscall) + let rule = SeccompRule::new(syscall) + .and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom.entry(syscall) .or_insert_with(Vec::new) .push(rule); } @@ -276,13 +270,10 @@ impl SystemIO { /// it's possible that the fd will be reused and therefore may be written to. #[allow(clippy::missing_panics_doc)] pub fn allow_file_write(mut self, file: &File) -> SystemIO { - let fd = file - .as_raw_fd() - .try_into() - .expect("provided fd was negative"); - let rule = SeccompRule::new(Sysno::write).and_condition(seccomp_arg_filter!(arg0 == fd)); - self.custom - .entry(Sysno::write) + let fd = file.as_raw_fd().try_into().expect("provided fd was negative"); + let rule = SeccompRule::new(Sysno::write) + .and_condition(seccomp_arg_filter!(arg0 == fd)); + self.custom.entry(Sysno::write) .or_insert_with(Vec::new) .push(rule); @@ -315,9 +306,7 @@ impl RuleSet for SystemIO { impl SystemIO { fn insert_flags>(&mut self, path: P, new_flags: BitFlags) { let path = path.as_ref().to_path_buf(); - let _flag = self - .landlock_rules - .entry(path.clone()) + let _flag = self.landlock_rules.entry(path.clone()) .and_modify(|existing_flags| existing_flags.access_rules.insert(new_flags)) .or_insert_with(|| LandlockRule::new(&path, new_flags)); } @@ -336,8 +325,7 @@ impl SystemIO { self.allow_close() .allow_read() .allow_metadata() - .allow_open() - .yes_really() + .allow_open().yes_really() } /// Use Landlock to allow only the specified file to be written to. If this function is called @@ -353,8 +341,7 @@ impl SystemIO { self.allow_close() .allow_write() .allow_metadata() - .allow_open() - .yes_really() + .allow_open().yes_really() } /// Use Landlock to allow files to be created in the given directory. If this function is called @@ -383,8 +370,7 @@ impl SystemIO { self.allow_metadata() .allow_close() .allow_ioctl() - .allow_open() - .yes_really() + .allow_open().yes_really() } /// Use Landlock to allow creating directories. If this function is called multiple times, all @@ -453,28 +439,20 @@ impl SystemIO { self.allow_close() .allow_read() .allow_metadata() - .allow_open() - .yes_really() + .allow_open().yes_really() } /// Use Landlock to allow access to DNS files, like /etc/resolv.conf pub fn allow_dns_files(mut self) -> SystemIO { let new_flags = access::read_path(); // TODO: libnss exec perms? - for path in &[ - "/etc/resolv.conf", - "/etc/hosts", - "/etc/host.conf", - "/etc/nsswitch.conf", - "/etc/gai.conf", - ] { + for path in &["/etc/resolv.conf", "/etc/hosts", "/etc/host.conf", "/etc/nsswitch.conf", "/etc/gai.conf"] { self.insert_flags(path, new_flags); } // allow relevant syscalls as well self.allow_close() .allow_read() .allow_metadata() - .allow_open() - .yes_really() + .allow_open().yes_really() } } diff --git a/src/builtins/time.rs b/src/builtins/time.rs index 81927ef..3b22ba9 100644 --- a/src/builtins/time.rs +++ b/src/builtins/time.rs @@ -1,11 +1,11 @@ //! Contains a [`RuleSet`] for allowing time-related syscalls, but check the comments for why you //! probably don't actually need to enable them. -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use crate::syscalls::Sysno; -use crate::RuleSet; +use crate::{SeccompRule, RuleSet}; #[must_use] /// Enable syscalls related to time. @@ -22,10 +22,10 @@ impl Time { } } - /// On most 64 bit systems glibc and musl both use the - /// [`vDSO`](https://man7.org/linux/man-pages/man7/vdso.7.html) to compute the time directly with - /// rdtsc rather than calling the `clock_gettime` syscall, so in most cases you don't need to - /// actually enable this. +/// On most 64 bit systems glibc and musl both use the +/// [`vDSO`](https://man7.org/linux/man-pages/man7/vdso.7.html) to compute the time directly with +/// rdtsc rather than calling the `clock_gettime` syscall, so in most cases you don't need to +/// actually enable this. pub fn allow_gettime(mut self) -> Time { self.allowed .extend([Sysno::clock_gettime, Sysno::clock_getres]); diff --git a/src/error.rs b/src/error.rs index 2c55120..2174767 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,10 +7,10 @@ use std::path::PathBuf; use seccompiler::Error as SeccompilerError; -#[cfg(feature = "landlock")] -use landlock::PathFdError; #[cfg(feature = "landlock")] use landlock::RulesetError as LandlockError; +#[cfg(feature = "landlock")] +use landlock::PathFdError; #[derive(Debug)] /// The error type produced by [`crate::SafetyContext`] diff --git a/src/landlock.rs b/src/landlock.rs index aa33e6f..3ce20a8 100644 --- a/src/landlock.rs +++ b/src/landlock.rs @@ -5,10 +5,7 @@ use std::path::{Path, PathBuf}; pub use landlock::RulesetError as LandlockError; -pub use landlock::{ - Access, AccessFs, BitFlags, CompatLevel, Compatible, PathBeneath, PathFd, Ruleset, RulesetAttr, - RulesetCreatedAttr, ABI, -}; +pub use landlock::{ABI, Access, AccessFs, BitFlags, Compatible, CompatLevel, PathBeneath, PathFd, Ruleset, RulesetAttr, RulesetCreatedAttr}; /// A Landlock rule. It consists of a path and a collection of access rights which determine what /// actions can be performed on that path. @@ -24,7 +21,10 @@ impl LandlockRule { /// Create a new Landlock Rule. pub fn new>(path: P, access_rules: BitFlags) -> LandlockRule { let path = path.as_ref().into(); - LandlockRule { path, access_rules } + LandlockRule { + path, + access_rules + } } } diff --git a/src/lib.rs b/src/lib.rs index 95190ea..4988d15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,17 +13,18 @@ //! See the [`SafetyContext`] struct's documentation and the tests/ and examples/ directories for //! more information on how to use it. + // Filter is the entire, top-level seccomp filter chain. All SeccompilerRules are or-ed together. // Vec<(i64, Vec)>, Vec is empty if Rule has no filters. // Rule is a syscall + multiple argument filters. All argument filters are and-ed together in a // single Rule. // ArgumentFilter is a single condition on a single argument // Comparator is used in an ArgumentFilter to choose the comparison operation -pub use seccompiler::Error as SeccompilerError; -pub use seccompiler::SeccompCmpOp as SeccompilerComparator; -pub use seccompiler::SeccompCondition as SeccompilerArgumentFilter; pub use seccompiler::SeccompFilter as SeccompilerFilter; pub use seccompiler::SeccompRule as SeccompilerRule; +pub use seccompiler::SeccompCondition as SeccompilerArgumentFilter; +pub use seccompiler::Error as SeccompilerError; +pub use seccompiler::SeccompCmpOp as SeccompilerComparator; use seccompiler::SeccompAction; @@ -43,9 +44,9 @@ mod landlock; #[cfg(feature = "landlock")] pub use landlock::*; -use std::collections::{BTreeMap, HashMap}; #[cfg(feature = "landlock")] use std::path::PathBuf; +use std::collections::{BTreeMap, HashMap}; #[derive(Debug, Clone, PartialEq)] /// A restriction on the arguments of a syscall. May be combined with other @@ -86,11 +87,7 @@ impl SeccompArgumentFilter { #[must_use] /// Create a new [`SeccompArgumentFilter`]. You should probably use the [`seccomp_arg_filter!`] /// instead. - pub fn new( - arg_idx: u8, - comparator: SeccompilerComparator, - value: u64, - ) -> SeccompArgumentFilter { + pub fn new(arg_idx: u8, comparator: SeccompilerComparator, value: u64) -> SeccompArgumentFilter { // TODO: add quirks mode file and check whether syscall's parameter at index `arg_idx` is // 32 or 64 bit (and also I guess if it even has that many arguments) SeccompArgumentFilter::new64(arg_idx, comparator, value) @@ -99,11 +96,7 @@ impl SeccompArgumentFilter { #[must_use] /// Create a new [`SeccompArgumentFilter`] that checks all 64 bits of the provided argument. /// You should probably use the [`seccomp_arg_filter!`] instead. - pub fn new64( - arg_idx: u8, - comparator: SeccompilerComparator, - value: u64, - ) -> SeccompArgumentFilter { + pub fn new64(arg_idx: u8, comparator: SeccompilerComparator, value: u64) -> SeccompArgumentFilter { SeccompArgumentFilter { arg_idx, comparator, @@ -116,11 +109,7 @@ impl SeccompArgumentFilter { /// Create a new [`SeccompArgumentFilter`] that checks 32 bits of the provided argument. /// You should probably use the [`seccomp_arg_filter!`] instead. See the struct's documentation /// for why this is needed. - pub fn new32( - arg_idx: u8, - comparator: SeccompilerComparator, - value: u32, - ) -> SeccompArgumentFilter { + pub fn new32(arg_idx: u8, comparator: SeccompilerComparator, value: u32) -> SeccompArgumentFilter { // Note that it doesn't matter if we convert with or without sign extension here since the // point is that we'll only compare the least significant 32 bits anyway. let value = u64::from(value); @@ -134,17 +123,9 @@ impl SeccompArgumentFilter { pub(crate) fn into_seccompiler(self) -> Result { use seccompiler::SeccompCmpArgLen; - let arg_len = if self.is_64bit { - SeccompCmpArgLen::Qword - } else { - SeccompCmpArgLen::Dword - }; - Ok(SeccompilerArgumentFilter::new( - self.arg_idx, - arg_len, - self.comparator, - self.value, - )?) + let arg_len = if self.is_64bit { SeccompCmpArgLen::Qword } else { SeccompCmpArgLen::Dword }; + Ok(SeccompilerArgumentFilter::new(self.arg_idx, arg_len, + self.comparator, self.value)?) } } @@ -184,11 +165,8 @@ impl SeccompRule { return Ok(None); } - let argument_filters: Vec = self - .argument_filters - .into_iter() - .map(SeccompArgumentFilter::into_seccompiler) - .collect::>()?; + let argument_filters: Vec = self.argument_filters.into_iter() + .map(SeccompArgumentFilter::into_seccompiler).collect::>()?; Ok(Some(SeccompilerRule::new(argument_filters)?)) } @@ -313,10 +291,13 @@ impl SafetyContext { let mut rules = rules.conditional_rules(); for syscall in base_syscalls { let rule = SeccompRule::new(syscall); - rules.entry(syscall).or_insert_with(Vec::new).push(rule); + rules.entry(syscall) + .or_insert_with(Vec::new) + .push(rule); } - rules.into_values().flatten().collect() + rules.into_values().flatten() + .collect() } /// Enable the simple and conditional rules provided by the [`RuleSet`]. @@ -336,18 +317,12 @@ impl SafetyContext { #[cfg(feature = "landlock")] fn enable_landlock_rules(&mut self, policy: &R) -> Result<(), ExtraSafeError> { let name = policy.name(); - let rules = policy - .landlock_rules() - .into_iter() + let rules = policy.landlock_rules().into_iter() .map(|rule| (rule.path.clone(), LabeledLandlockRule(name, rule))); for (path, labeled_rule) in rules { if let Some(existing_rule) = self.landlock_rules.get(&path) { - return Err(ExtraSafeError::DuplicatePath( - path.clone(), - existing_rule.0, - labeled_rule.0, - )); + return Err(ExtraSafeError::DuplicatePath(path.clone(), existing_rule.0, labeled_rule.0)); } // value here is always none because we checked above that we're not inserting a path // that already exists @@ -381,7 +356,8 @@ impl SafetyContext { labeled_existing_rule.0, labeled_new_rule.0, )); - } else if !new_is_simple && existing_is_simple { + } + else if !new_is_simple && existing_is_simple { return Err(ExtraSafeError::ConditionalNoEffectError( new_rule.syscall, labeled_new_rule.0, @@ -549,10 +525,7 @@ impl SafetyContext { // should be allowed without restriction } let result = rules_map.insert(syscall, seccompiler_rules); - assert!( - result.is_none(), - "extrasafe logic error: somehow inserted the same syscall's rules twice" - ); + assert!(result.is_none(), "extrasafe logic error: somehow inserted the same syscall's rules twice"); } #[cfg(not(target_os = "linux"))] @@ -562,16 +535,15 @@ impl SafetyContext { rules_map, SeccompAction::Errno(self.errno), SeccompAction::Allow, - std::env::consts::ARCH - .try_into() - .expect("invalid arches are prevented above"), + std::env::consts::ARCH.try_into().expect("invalid arches are prevented above"), )?; let bpf_filter: seccompiler::BpfProgram = seccompiler_filter.try_into()?; if self.all_threads { seccompiler::apply_filter_all_threads(&bpf_filter)?; - } else { + } + else { seccompiler::apply_filter(&bpf_filter)?; } @@ -580,11 +552,11 @@ impl SafetyContext { #[cfg(feature = "landlock")] fn apply_landlock_rules(&self) -> Result<(), ExtraSafeError> { - let abi = ABI::V2; - let mut landlock_ruleset = Ruleset::default() + let abi = ABI::V2; + let mut landlock_ruleset = Ruleset::default() .set_compatibility(CompatLevel::HardRequirement) - .handle_access(AccessFs::from_all(abi))? - .create()?; + .handle_access(AccessFs::from_all(abi))? + .create()?; for LabeledLandlockRule(_policy_name, rule) in self.landlock_rules.values() { // If path does not exist or is not accessible, just ignore it @@ -593,7 +565,7 @@ impl SafetyContext { landlock_ruleset = landlock_ruleset.add_rule(path_beneath)?; } } - let _status = landlock_ruleset.restrict_self(); + let _status = landlock_ruleset.restrict_self(); Ok(()) } } diff --git a/src/macros.rs b/src/macros.rs index b9d961f..242c645 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,86 +19,40 @@ #[macro_export] macro_rules! seccomp_arg_filter { ($argno:ident <= $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Le, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Le, $value) }; ($argno:ident < $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Lt, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Lt, $value) }; ($argno:ident >= $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Ge, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Ge, $value) }; ($argno:ident > $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Gt, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Gt, $value) }; ($argno:ident == $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Eq, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Eq, $value) }; ($argno:ident != $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::Ne, - $value, - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::Ne, $value) }; ($argno:ident & $mask:tt == $value:expr) => { - $crate::SeccompArgumentFilter::new( - match_argno!($argno), - $crate::SeccompilerComparator::MaskedEq($mask), - $value, - ) - }; - ($_other:expr) => { - compile_error!( - "usage: `arg[0-5] {<=, <, >=, >, ==, !=} ` or `arg[0-5] & == `" - ) + $crate::SeccompArgumentFilter::new(match_argno!($argno), $crate::SeccompilerComparator::MaskedEq($mask), $value) }; + ($_other:expr) => {compile_error!("usage: `arg[0-5] {<=, <, >=, >, ==, !=} ` or `arg[0-5] & == `")}; } #[doc(hidden)] #[macro_export] /// Internal macro for `seccomp_arg_filter!` macro_rules! match_argno { - (arg0) => { - 0 - }; - (arg1) => { - 1 - }; - (arg2) => { - 2 - }; - (arg3) => { - 3 - }; - (arg4) => { - 4 - }; - (arg5) => { - 5 - }; - ($_other:expr) => { - compile_error!("Seccomp argument filters must start with argX where X is 0-5") - }; + (arg0) => {0}; + (arg1) => {1}; + (arg2) => {2}; + (arg3) => {3}; + (arg4) => {4}; + (arg5) => {5}; + ($_other:expr) => {compile_error!("Seccomp argument filters must start with argX where X is 0-5")}; } /// These tests just test that the macro expands correctly, not that the comparators do what they diff --git a/tests/arg_comparisons.rs b/tests/arg_comparisons.rs index cfff972..c47f1cc 100644 --- a/tests/arg_comparisons.rs +++ b/tests/arg_comparisons.rs @@ -1,8 +1,8 @@ #![allow(unsafe_code)] // allow unsafe to call syscalls directly -use builtins::SystemIO; use extrasafe::*; +use builtins::SystemIO; use extrasafe::syscalls::Sysno; use std::collections::HashMap; @@ -16,7 +16,8 @@ impl RuleSet for IoctlRestricted64 { fn conditional_rules(&self) -> HashMap> { let cmp = SeccompArgumentFilter::new64(1, SeccompilerComparator::Eq, self.0); - let rule = SeccompRule::new(Sysno::ioctl).and_condition(cmp); + let rule = SeccompRule::new(Sysno::ioctl) + .and_condition(cmp); HashMap::from([(Sysno::ioctl, vec![rule])]) } @@ -35,7 +36,8 @@ impl RuleSet for IoctlRestricted32 { fn conditional_rules(&self) -> HashMap> { let cmp = SeccompArgumentFilter::new32(1, SeccompilerComparator::Eq, self.0); - let rule = SeccompRule::new(Sysno::ioctl).and_condition(cmp); + let rule = SeccompRule::new(Sysno::ioctl) + .and_condition(cmp); HashMap::from([(Sysno::ioctl, vec![rule])]) } @@ -45,6 +47,7 @@ impl RuleSet for IoctlRestricted32 { } } + struct GetUidRestricted; impl RuleSet for GetUidRestricted { fn simple_rules(&self) -> Vec { @@ -74,12 +77,11 @@ fn cmp_arg_syscall_unused_parameter() { assert!(uid1 > 0); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(GetUidRestricted) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .enable(GetUidRestricted).unwrap() + .apply_to_current_thread().unwrap(); // SAFETY: getuid just gives the current user's uid let uid2 = unsafe { libc::getuid() }; @@ -90,7 +92,7 @@ macro_rules! assert_errno { ($e: expr) => { let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); assert_eq!(errno, $e); - }; + } } // See https://github.com/rust-vmm/seccompiler/issues/59 for more details on below two tests @@ -100,12 +102,11 @@ fn cmp_arg_64bit_ioctl_musl_glibc_diff() { let seccomp_errno = 999; extrasafe::SafetyContext::new() .with_errno(seccomp_errno) - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(IoctlRestricted64(value)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .enable(IoctlRestricted64(value)).unwrap() + .apply_to_current_thread().unwrap(); // On glibc, the second parameter is a u64, so the value seen by the kernel matches the value // in our seccomp filter, and the ioctl call is allowed. It then sets errno to -9 since 4321 is @@ -133,12 +134,11 @@ fn cmp_arg_32bit_ioctl_musl_glibc_same() { let seccomp_errno = 999; extrasafe::SafetyContext::new() .with_errno(seccomp_errno) - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(IoctlRestricted32(value)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .enable(IoctlRestricted32(value)).unwrap() + .apply_to_current_thread().unwrap(); // here both tests are the same except for value being i32 on musl #[cfg(target_env = "gnu")] diff --git a/tests/bad_combination.rs b/tests/bad_combination.rs index 964f8c8..b44071b 100644 --- a/tests/bad_combination.rs +++ b/tests/bad_combination.rs @@ -24,8 +24,8 @@ impl RuleSet for JustWrite { /// (This is because the simple rule would override the conditional one) fn invalid_combination_new_simple() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout()) - .unwrap() + .enable(SystemIO::nothing() + .allow_stdout()).unwrap() .enable(SystemIO::everything()); assert!( @@ -40,13 +40,10 @@ fn invalid_combination_new_simple() { #[test] fn invalid_combination_new_conditional() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::everything()) - .unwrap() - .enable(SystemIO::nothing().allow_stdout()); - assert!( - res.is_err(), - "Extrasafe didn't fail when adding conflicting rules" - ); + .enable(SystemIO::everything()).unwrap() + .enable(SystemIO::nothing() + .allow_stdout()); + assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `write` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `SystemIO`."); @@ -56,8 +53,8 @@ fn invalid_combination_new_conditional() { /// same as above but with different rulesets to check the error message fn invalid_combination_new_simple_different_name() { let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout()) - .unwrap() + .enable(SystemIO::nothing() + .allow_stdout()).unwrap() .enable(JustWrite); assert!( res.is_err(), @@ -71,13 +68,10 @@ fn invalid_combination_new_simple_different_name() { #[test] fn invalid_combination_new_conditional_different_name() { let res = extrasafe::SafetyContext::new() - .enable(JustWrite) - .unwrap() - .enable(SystemIO::nothing().allow_stdout()); - assert!( - res.is_err(), - "Extrasafe didn't fail when adding conflicting rules" - ); + .enable(JustWrite).unwrap() + .enable(SystemIO::nothing() + .allow_stdout()); + assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `write` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `JustWrite`."); @@ -86,12 +80,13 @@ fn invalid_combination_new_conditional_different_name() { #[test] /// Test that adding a conditional and simple rule in the same `RuleSet` produces an error fn invalid_combination_read_and_stdin() { - let res = - extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_read().allow_stdin()); - assert!( - res.is_err(), - "Extrasafe didn't fail when adding conflicting rules" - ); + + let res = extrasafe::SafetyContext::new() + .enable(SystemIO::nothing() + .allow_read() + .allow_stdin() + ); + assert!(res.is_err(), "Extrasafe didn't fail when adding conflicting rules"); let err = res.unwrap_err(); assert_eq!(err.to_string(), "A conditional rule on syscall `read` from RuleSet `SystemIO` would be overridden by a simple rule from RuleSet `SystemIO`."); @@ -100,7 +95,12 @@ fn invalid_combination_read_and_stdin() { #[test] /// Test that adding duplicate simple rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_simple() { - let res = extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_read().allow_read()); + + let res = extrasafe::SafetyContext::new() + .enable(SystemIO::nothing() + .allow_read() + .allow_read() + ); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -110,10 +110,13 @@ fn not_invalid_combination_duplicate_simple() { #[test] /// Test that adding duplicate simple rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_simple2() { + let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_read()) - .unwrap() - .enable(SystemIO::nothing().allow_read()); + .enable(SystemIO::nothing() + .allow_read()).unwrap() + .enable(SystemIO::nothing() + .allow_read() + ); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -123,8 +126,12 @@ fn not_invalid_combination_duplicate_simple2() { #[test] /// Test that adding duplicate conditional rules in the same `RuleSet` doesn't produce an error fn not_invalid_combination_duplicate_conditional() { - let res = - extrasafe::SafetyContext::new().enable(SystemIO::nothing().allow_stdin().allow_stdin()); + + let res = extrasafe::SafetyContext::new() + .enable(SystemIO::nothing() + .allow_stdin() + .allow_stdin() + ); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); @@ -134,10 +141,14 @@ fn not_invalid_combination_duplicate_conditional() { #[test] /// Test that adding duplicate conditional rules from different `RuleSet`s doesn't produce an error fn not_invalid_combination_duplicate_conditional2() { + let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdin()) - .unwrap() - .enable(SystemIO::nothing().allow_stdin()); + .enable(SystemIO::nothing() + .allow_stdin() + ).unwrap() + .enable(SystemIO::nothing() + .allow_stdin() + ); assert!(res.is_ok()); let res = res.unwrap().apply_to_current_thread(); diff --git a/tests/default_deny.rs b/tests/default_deny.rs index c84cea2..0efa8da 100644 --- a/tests/default_deny.rs +++ b/tests/default_deny.rs @@ -22,8 +22,7 @@ fn filesystem_no_read() { drop(file); let res = extrasafe::SafetyContext::new() - .enable(Basic) - .unwrap() + .enable(Basic).unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); @@ -48,8 +47,7 @@ fn filesystem_no_write() { let mut file = tempfile().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(Basic) - .unwrap() + .enable(Basic).unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); @@ -74,8 +72,7 @@ fn filesystem_no_create() { let dir = tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(Basic) - .unwrap() + .enable(Basic).unwrap() .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); diff --git a/tests/inherit_filters.rs b/tests/inherit_filters.rs index 31bfb72..fdf5ca2 100644 --- a/tests/inherit_filters.rs +++ b/tests/inherit_filters.rs @@ -1,14 +1,15 @@ //! Tests that demonstrate seccomp filters are inherited by child processes. -use extrasafe::builtins::danger_zone::{ForkAndExec, Threads}; use extrasafe::SafetyContext; +use extrasafe::builtins::danger_zone::{ForkAndExec, Threads}; #[test] /// Enable seccomp *only on this thread*, create a new thread, try to create a file and check that /// it fails. fn new_thread_inherits_restrictions() { SafetyContext::new() - .enable(Threads::nothing().allow_create()) + .enable(Threads::nothing() + .allow_create()) .unwrap() .apply_to_current_thread() .unwrap(); diff --git a/tests/landlock_allthreads_fail.rs b/tests/landlock_allthreads_fail.rs index d4cf1dd..5a26890 100644 --- a/tests/landlock_allthreads_fail.rs +++ b/tests/landlock_allthreads_fail.rs @@ -9,17 +9,13 @@ fn test_landlock_apply_to_all_fails() { let dir = tempfile::tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_read_path(&dir)) - .unwrap() + .enable( + SystemIO::nothing() + .allow_read_path(&dir) + ).unwrap() .landlock_only() .apply_to_all_threads(); - assert!( - res.is_err(), - "Did not error when applying to all threads with landlock rules" - ); - assert!(res - .unwrap_err() - .to_string() - .contains("Landlock does not support syncing to all threads")); + assert!(res.is_err(), "Did not error when applying to all threads with landlock rules"); + assert!(res.unwrap_err().to_string().contains("Landlock does not support syncing to all threads")); } diff --git a/tests/landlock_basic.rs b/tests/landlock_basic.rs index 09c51f7..05c4fce 100644 --- a/tests/landlock_basic.rs +++ b/tests/landlock_basic.rs @@ -2,19 +2,15 @@ use std::path::Path; -use std::fs::{create_dir, read_dir, remove_dir, remove_file, File}; use std::io::{Read, Write}; +use std::fs::{create_dir, read_dir, remove_dir, remove_file, File}; use extrasafe::builtins::SystemIO; /// helper to check a file can be read fn can_read_file(path: &Path, expected_data: &str) { let res = File::open(path); - assert!( - res.is_ok(), - "Failed to open allowed file: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to open allowed file: {:?}", res.unwrap_err()); let mut f = res.unwrap(); let mut file_contents = String::new(); @@ -27,20 +23,12 @@ fn can_read_file(path: &Path, expected_data: &str) { /// helper to check a file can be written to fn can_write_file(path: &Path, write_data: &str) { let res = File::options().append(true).open(path); - assert!( - res.is_ok(), - "Failed to open allowed file: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to open allowed file: {:?}", res.unwrap_err()); let mut f = res.unwrap(); let res = f.write_all(write_data.as_bytes()); - assert!( - res.is_ok(), - "failed to write to file: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "failed to write to file: {:?}", res.unwrap_err()); drop(f); // after writing the data, check we can read it back @@ -50,10 +38,7 @@ fn can_write_file(path: &Path, write_data: &str) { /// helper to check a file cannot be opened fn can_not_open_file(path: &Path) { let res = File::open(path); - assert!( - res.is_err(), - "Incorrectly succeeded in opening file for reading" - ); + assert!(res.is_err(), "Incorrectly succeeded in opening file for reading"); } // TODO: distinguish between not being able to remove dir due to not being empty vs denied via @@ -80,10 +65,11 @@ fn test_landlock_read_file() { drop(f); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_read_path(&allowed_file)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + SystemIO::nothing() + .allow_read_path(&allowed_file) + ).unwrap() + .apply_to_current_thread().unwrap(); // read allowed, fail to open denied can_read_file(&allowed_file, "test allowed"); @@ -103,11 +89,9 @@ fn test_landlock_write_file() { .enable( SystemIO::nothing() .allow_write_file(&dir) - .allow_read_path(&dir), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .allow_read_path(&dir) + ).unwrap() + .apply_to_current_thread().unwrap(); can_write_file(&allowed_file, "test data"); @@ -126,24 +110,18 @@ fn test_landlock_create_file_in_path() { let denied_file = dir_denied.path().join("denied.txt"); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_create_in_dir(&dir_allowed)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + SystemIO::nothing() + .allow_create_in_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); // create succeeds in one directory, fails in other let res = File::create(allowed_file); - assert!( - res.is_ok(), - "Failed to create file in allowed directory: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to create file in allowed directory: {:?}", res.unwrap_err()); let res = File::create(denied_file); - assert!( - res.is_err(), - "Incorrectly suceeded in creating file in directory we did not allow" - ); + assert!(res.is_err(), "Incorrectly suceeded in creating file in directory we did not allow"); } #[test] @@ -164,11 +142,9 @@ fn test_landlock_delete_file() { .enable( SystemIO::nothing() .allow_remove_file(&dir_allowed) - .allow_list_dir(&dir_allowed), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .allow_list_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); let res = remove_file(&allowed_file); assert!(res.is_ok(), "Failed to remove file: {}", res.unwrap_err()); @@ -177,10 +153,7 @@ fn test_landlock_delete_file() { assert_eq!(dir.collect::>().len(), 0); let res = remove_file(&denied_file); - assert!( - res.is_err(), - "Incorrectly succeeded in removing file that was not allowed" - ); + assert!(res.is_err(), "Incorrectly succeeded in removing file that was not allowed"); } #[test] @@ -202,10 +175,11 @@ fn test_landlock_read_dir() { drop(f); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_read_path(allowed_subdir)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + SystemIO::nothing() + .allow_read_path(allowed_subdir) + ).unwrap() + .apply_to_current_thread().unwrap(); // read allowed, fail to open denied can_read_file(&allowed_file, "test allowed"); @@ -219,10 +193,11 @@ fn test_landlock_create_dir() { let dir_denied = tempfile::tempdir().unwrap(); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_create_dir(&dir_allowed)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + SystemIO::nothing() + .allow_create_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); let allowed_subdir = dir_allowed.path().join("test_allowed"); let res = create_dir(allowed_subdir); @@ -244,26 +219,20 @@ fn test_landlock_list_dir() { drop(f); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_list_dir(&dir_allowed)) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable( + SystemIO::nothing() + .allow_list_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); let res = read_dir(&dir_allowed); - assert!( - res.is_ok(), - "Failed to list directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to list directory: {}", res.unwrap_err()); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); let res = read_dir(&dir_denied); - assert!( - res.is_err(), - "Incorrectly succeeded in reading directory that was not allowed" - ); + assert!(res.is_err(), "Incorrectly succeeded in reading directory that was not allowed"); } #[test] @@ -282,24 +251,15 @@ fn test_landlock_delete_dir() { .enable( SystemIO::nothing() .allow_remove_dir(&dir_allowed) - .allow_list_dir(&dir_allowed), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .allow_list_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); let res = remove_dir(&allowed_subdir); - assert!( - res.is_ok(), - "Failed to remove directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to remove directory: {}", res.unwrap_err()); let res = remove_dir(&denied_subdir); - assert!( - res.is_err(), - "Incorrectly succeeded in removing directory that was not allowed" - ); + assert!(res.is_err(), "Incorrectly succeeded in removing directory that was not allowed"); // check dir is empty let mut dir = read_dir(&dir_allowed).unwrap(); @@ -329,11 +289,9 @@ fn test_landlock_one_ruleset() { .allow_list_dir(&allowed_subdir_write) .allow_read_path(&allowed_subdir_write) .allow_write_file(&allowed_subdir_write) - .allow_remove_dir(&dir_allowed), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .allow_remove_dir(&dir_allowed) + ).unwrap() + .apply_to_current_thread().unwrap(); // create file in write dir let allowed_file = allowed_subdir_write.as_path().join("allowed.txt"); @@ -343,31 +301,19 @@ fn test_landlock_one_ruleset() { // check we can list ro directory let res = read_dir(&allowed_subdir); - assert!( - res.is_ok(), - "Failed to list ro directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to list ro directory: {}", res.unwrap_err()); let mut dir = res.unwrap(); assert!(dir.next().is_none()); // check we can list rw directory let res = read_dir(&allowed_subdir_write); - assert!( - res.is_ok(), - "Failed to list rw directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to list rw directory: {}", res.unwrap_err()); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); // check we can remove ro directory (even though we can't write to it!) let res = remove_dir(&allowed_subdir); - assert!( - res.is_ok(), - "Failed to remove ro directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to remove ro directory: {}", res.unwrap_err()); // check we can read file we wrote to can_read_file(&allowed_file, "test allowed write directory"); @@ -396,18 +342,15 @@ fn test_landlock_different_rulesets() { .allow_list_dir(&allowed_subdir) .allow_read_path(&allowed_subdir) .allow_remove_dir(&dir_allowed) - .allow_list_dir(&dir_allowed), - ) - .unwrap() + .allow_list_dir(&dir_allowed) + ).unwrap() .enable( SystemIO::nothing() .allow_create_in_dir(&allowed_subdir_write) .allow_read_path(&allowed_subdir_write) - .allow_write_file(&allowed_subdir_write), - ) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .allow_write_file(&allowed_subdir_write) + ).unwrap() + .apply_to_current_thread().unwrap(); // create file in write dir let allowed_file = allowed_subdir_write.as_path().join("allowed.txt"); @@ -417,31 +360,19 @@ fn test_landlock_different_rulesets() { // check we can list ro directory let res = read_dir(&allowed_subdir); - assert!( - res.is_ok(), - "Failed to list ro directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to list ro directory: {}", res.unwrap_err()); let mut dir = res.unwrap(); assert!(dir.next().is_none()); // check we can list rw directory let res = read_dir(&allowed_subdir_write); - assert!( - res.is_ok(), - "Failed to list rw directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to list rw directory: {}", res.unwrap_err()); let dir = res.unwrap(); assert_eq!(dir.collect::>().len(), 1); // check we can remove ro directory (even though we can't write to it!) let res = remove_dir(&allowed_subdir); - assert!( - res.is_ok(), - "Failed to remove ro directory: {}", - res.unwrap_err() - ); + assert!(res.is_ok(), "Failed to remove ro directory: {}", res.unwrap_err()); // check we can read file we wrote to can_read_file(&allowed_file, "test allowed write directory"); @@ -457,16 +388,14 @@ fn test_nonexistant_file_no_error() { let nonexistant = dir.path().join("bad"); let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_create_in_dir(nonexistant)) - .unwrap() + .enable( + SystemIO::nothing() + .allow_create_in_dir(nonexistant) + ).unwrap() .landlock_only() .apply_to_current_thread(); - assert!( - res.is_ok(), - "Errored when passing nonexistant file to landlock rule: {:?} ", - res.unwrap_err() - ); + assert!(res.is_ok(), "Errored when passing nonexistant file to landlock rule: {:?} ", res.unwrap_err()); } #[test] @@ -475,17 +404,17 @@ fn test_duplicate_path() { let dir = tempfile::tempdir().unwrap(); let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_create_in_dir(&dir)) - .unwrap() - .enable(SystemIO::nothing().allow_create_in_dir(&dir)); - - assert!( - res.is_err(), - "Did not error on passing same dir in multiple rulesets" - ); + .enable( + SystemIO::nothing() + .allow_create_in_dir(&dir) + ).unwrap() + .enable( + SystemIO::nothing() + .allow_create_in_dir(&dir) + ); + + assert!(res.is_err(), "Did not error on passing same dir in multiple rulesets"); let err = res.unwrap_err(); assert!(err.to_string().contains("The same path")); - assert!(err - .to_string() - .contains("was used in two different landlock rules.")); + assert!(err.to_string().contains("was used in two different landlock rules.")); } diff --git a/tests/landlock_conflicts.rs b/tests/landlock_conflicts.rs index c4743bc..52400c8 100644 --- a/tests/landlock_conflicts.rs +++ b/tests/landlock_conflicts.rs @@ -1,6 +1,6 @@ #![cfg(feature = "landlock")] -use std::fs::{read_dir, File}; +use std::fs::{File, read_dir}; use extrasafe::builtins::SystemIO; @@ -11,44 +11,46 @@ fn landlock_with_seccomp_arg_filters_fails() { let path = tempfile::tempdir().unwrap(); // same ruleset - let res = extrasafe::SafetyContext::new().enable( - SystemIO::nothing() - .allow_open_readonly() - .allow_list_dir(&path), - ); - - assert!( - res.is_err(), - "Enabling filter succeeded with landlock and seccomp arg-restricted open" - ); + let res = extrasafe::SafetyContext::new() + .enable( + SystemIO::nothing() + .allow_open_readonly() + .allow_list_dir(&path) + ); + + assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert_eq!(err.to_string().contains("xxx")); // different rulesets, landlock first let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_open_readonly()) - .unwrap() - .enable(SystemIO::nothing().allow_read_path(&path)); - - assert!( - res.is_err(), - "Enabling filter succeeded with landlock and seccomp arg-restricted open" - ); + .enable( + SystemIO::nothing() + .allow_open_readonly() + ).unwrap() + .enable( + SystemIO::nothing() + .allow_read_path(&path) + ); + + assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert!(err.to_string().contains("xxx")); // different rulesets, seccomp first let res = extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_read_path(&path)) - .unwrap() - .enable(SystemIO::nothing().allow_open_readonly()); - - assert!( - res.is_err(), - "Enabling filter succeeded with landlock and seccomp arg-restricted open" - ); + .enable( + SystemIO::nothing() + .allow_read_path(&path) + ).unwrap() + .enable( + SystemIO::nothing() + .allow_open_readonly() + ); + + assert!(res.is_err(), "Enabling filter succeeded with landlock and seccomp arg-restricted open"); // TODO: seccomp/landlock clash error reporting // let err = res.unwrap_err(); // assert!(err.to_string().contains("xxx")); @@ -62,10 +64,7 @@ fn landlock_only() { .landlock_only() .apply_to_current_thread(); - assert!( - res.is_err(), - "extrasafe did not error when applying with no seccomp or landlock rules" - ); + assert!(res.is_err(), "extrasafe did not error when applying with no seccomp or landlock rules"); let err = res.unwrap_err(); assert!(err.to_string().contains("No rules were enabled")); @@ -73,11 +72,12 @@ fn landlock_only() { let dir = tempfile::tempdir().unwrap(); extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_create_in_dir(&dir)) - .unwrap() + .enable( + SystemIO::nothing() + .allow_create_in_dir(&dir) + ).unwrap() .landlock_only() - .apply_to_current_thread() - .unwrap(); + .apply_to_current_thread().unwrap(); // test that we can run arbitrary syscalls let pid = unsafe { libc::getpid() }; @@ -86,21 +86,11 @@ fn landlock_only() { // test that we can create in the given directory let file_path = dir.path().join("okay.txt"); let file_res = File::create(file_path); - assert!( - file_res.is_ok(), - "Failed to create file in allowed dir: {:?}", - file_res.unwrap_err() - ); + assert!(file_res.is_ok(), "Failed to create file in allowed dir: {:?}", file_res.unwrap_err()); // test that we can't list paths let list_res = read_dir(&dir); - assert!( - list_res.is_err(), - "Incorrectly succeeded in listing directory" - ); + assert!(list_res.is_err(), "Incorrectly succeeded in listing directory"); let list_res = read_dir("/etc"); - assert!( - list_res.is_err(), - "Incorrectly succeeded in listing directory" - ); + assert!(list_res.is_err(), "Incorrectly succeeded in listing directory"); } diff --git a/tests/multiple_conditions.rs b/tests/multiple_conditions.rs index f92aa22..298ff34 100644 --- a/tests/multiple_conditions.rs +++ b/tests/multiple_conditions.rs @@ -7,23 +7,16 @@ use extrasafe::*; /// or-ed together across all `RuleSets`. fn multiple_rulsets_conditional() { SafetyContext::new() - .enable(builtins::SystemIO::nothing().allow_stdout()) - .unwrap() - .enable(builtins::SystemIO::nothing().allow_stderr()) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(builtins::SystemIO::nothing() + .allow_stdout() + ).unwrap() + .enable(builtins::SystemIO::nothing() + .allow_stderr() + ).unwrap() + .apply_to_current_thread().unwrap(); let res = writeln!(std::io::stdout(), "we can print to stdout"); - assert!( - res.is_ok(), - "failed to write to stdout: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "failed to write to stdout: {:?}", res.unwrap_err()); let res = writeln!(std::io::stderr(), "we can print to stderr"); - assert!( - res.is_ok(), - "failed to write to stderr: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "failed to write to stderr: {:?}", res.unwrap_err()); } diff --git a/tests/multiple_filters.rs b/tests/multiple_filters.rs index 3ab4790..6b7f233 100644 --- a/tests/multiple_filters.rs +++ b/tests/multiple_filters.rs @@ -1,5 +1,5 @@ -use extrasafe::syscalls::Sysno; use extrasafe::*; +use extrasafe::syscalls::Sysno; use std::collections::HashMap; @@ -22,25 +22,23 @@ impl RuleSet for Seccomp { /// really doesn't ever make sense to enable multiple filters. fn filter_stacking_works_but_may_give_unintended_results() { SafetyContext::new() - .enable( - builtins::SystemIO::nothing() - .allow_stdout() - .allow_stderr() - .allow_open() - .yes_really() - .allow_metadata(), - ) - .unwrap() - .enable(Seccomp) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(builtins::SystemIO::nothing() + .allow_stdout() + .allow_stderr() + .allow_open().yes_really() + .allow_metadata() + ).unwrap() + .enable(Seccomp).unwrap() + .apply_to_current_thread().unwrap(); let res = SafetyContext::new() - .enable(builtins::SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(builtins::danger_zone::Threads::nothing().allow_create()) - .unwrap() + .enable(builtins::SystemIO::nothing() + .allow_stdout() + .allow_stderr() + ).unwrap() + .enable(builtins::danger_zone::Threads::nothing() + .allow_create() + ).unwrap() .apply_to_current_thread(); assert!( res.is_ok(), @@ -49,7 +47,8 @@ fn filter_stacking_works_but_may_give_unintended_results() { ); println!("test"); - let res = std::thread::Builder::new().spawn(|| println!("will not run")); + let res = std::thread::Builder::new() + .spawn(|| println!("will not run")); assert!(res.is_err(), "Even though clone was enabled on the second filter, it was not in the first and so isn't allowed."); let res = std::fs::File::open("will_not_be_opened.txt"); diff --git a/tests/network.rs b/tests/network.rs index 150c01f..dc0b92e 100644 --- a/tests/network.rs +++ b/tests/network.rs @@ -16,6 +16,7 @@ use std::thread; /// main thread, enable seccomp, send the message and get a response. Then try to bind a new socket /// and check that it fails. fn test_udp() { + // These block on send until reciever has finished recv. let (sender1, recv1) = sync_channel::<()>(0); @@ -46,10 +47,14 @@ fn test_udp() { // create safetycontext after server and client have been bound. SafetyContext::new() - .enable(Networking::nothing().allow_running_udp_sockets()) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .enable( + Networking::nothing() + .allow_running_udp_sockets() + ).unwrap() + .enable( + Threads::nothing() + .allow_create() + ).unwrap() .apply_to_current_thread() .unwrap(); @@ -110,10 +115,11 @@ fn test_tcp() { // create safetycontext after server and client have been bound. SafetyContext::new() - .enable(Networking::nothing().allow_running_tcp_clients()) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .enable(Networking::nothing() + .allow_running_tcp_clients() + ).unwrap() + .enable(Threads::nothing() + .allow_create()).unwrap() .apply_to_current_thread() .unwrap(); @@ -146,30 +152,28 @@ fn test_tcp() { /// instead open and bind before applying your policy. fn test_start_tcp() { SafetyContext::new() - .enable(Networking::nothing().allow_start_tcp_servers().yes_really()) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .enable( + Networking::nothing() + .allow_start_tcp_servers().yes_really() + ).unwrap() + .enable( + Threads::nothing() + .allow_create() + ).unwrap() .apply_to_current_thread() .unwrap(); let tcp_res = std::net::TcpListener::bind("127.0.0.1:0"); assert!(tcp_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); - assert!( - udp_res.is_err(), - "Incorrectly succeeded in binding udp socket" - ); + assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); // test ipv6 as well let tcp_res = std::net::TcpListener::bind("[::1]:0"); assert!(tcp_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("[::1]:0"); - assert!( - udp_res.is_err(), - "Incorrectly succeeded in binding udp socket" - ); + assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); } #[test] @@ -177,30 +181,28 @@ fn test_start_tcp() { /// instead open and bind before applying your policy. fn test_start_udp() { SafetyContext::new() - .enable(Networking::nothing().allow_start_udp_servers().yes_really()) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .enable( + Networking::nothing() + .allow_start_udp_servers().yes_really() + ).unwrap() + .enable( + Threads::nothing() + .allow_create() + ).unwrap() .apply_to_current_thread() .unwrap(); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); assert!(udp_res.is_ok(), "Failed to bind udp server"); let udp_res = std::net::TcpListener::bind("127.0.0.1:0"); - assert!( - udp_res.is_err(), - "Incorrectly succeeded in binding udp socket" - ); + assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); // test ipv6 as well let udp_res = std::net::UdpSocket::bind("[::1]:0"); assert!(udp_res.is_ok(), "Failed to bind udp server"); let tcp_res = std::net::TcpListener::bind("[::1]:0"); - assert!( - tcp_res.is_err(), - "Incorrectly succeeded in binding tcp socket" - ); + assert!(tcp_res.is_err(), "Incorrectly succeeded in binding tcp socket"); } #[test] @@ -214,12 +216,12 @@ fn test_start_unix() { SafetyContext::new() .enable( Networking::nothing() - .allow_start_unix_servers() - .yes_really(), - ) - .unwrap() - .enable(Threads::nothing().allow_create()) - .unwrap() + .allow_start_unix_servers().yes_really() + ).unwrap() + .enable( + Threads::nothing() + .allow_create() + ).unwrap() .apply_to_current_thread() .unwrap(); @@ -227,14 +229,8 @@ fn test_start_unix() { assert!(unix_res.is_ok(), "Failed to bind tcp server"); let udp_res = std::net::UdpSocket::bind("127.0.0.1:0"); - assert!( - udp_res.is_err(), - "Incorrectly succeeded in binding udp socket" - ); + assert!(udp_res.is_err(), "Incorrectly succeeded in binding udp socket"); let tcp_res = std::net::TcpListener::bind("[::1]:0"); - assert!( - tcp_res.is_err(), - "Incorrectly succeeded in binding tcp socket" - ); + assert!(tcp_res.is_err(), "Incorrectly succeeded in binding tcp socket"); } diff --git a/tests/ruleset_union.rs b/tests/ruleset_union.rs index d2e9775..2567bea 100644 --- a/tests/ruleset_union.rs +++ b/tests/ruleset_union.rs @@ -1,9 +1,10 @@ +use std::path::Path; use std::fs::File; use std::io::{Read, Write}; -use std::path::Path; -use builtins::SystemIO; use extrasafe::*; +use builtins::SystemIO; + // Tests to make sure we don't run into this issue // https://github.com/rust-vmm/seccompiler/issues/42 @@ -14,20 +15,18 @@ use extrasafe::*; fn different_rulesets_same_syscall() { SafetyContext::new() // First RuleSet: stdout, stderr + .enable(SystemIO::nothing() + .allow_read() + .allow_stdout() + .allow_stderr() + .allow_metadata() + ).unwrap() .enable( - SystemIO::nothing() - .allow_read() - .allow_stdout() - .allow_stderr() - .allow_metadata(), - ) - .unwrap() - .enable( - // Second RuleSet: stderr only - SystemIO::nothing() - .allow_stderr() - .allow_metadata() - .allow_close(), + // Second RuleSet: stderr only + SystemIO::nothing() + .allow_stderr() + .allow_metadata() + .allow_close(), ) .unwrap() .apply_to_current_thread() @@ -35,17 +34,9 @@ fn different_rulesets_same_syscall() { // Try to write to stdout and stderr let res = writeln!(std::io::stdout(), "we can print to stdout"); - assert!( - res.is_ok(), - "failed to write to stdout: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "failed to write to stdout: {:?}", res.unwrap_err()); let res = writeln!(std::io::stderr(), "we can print to stderr"); - assert!( - res.is_ok(), - "failed to write to stderr: {:?}", - res.unwrap_err() - ); + assert!(res.is_ok(), "failed to write to stderr: {:?}", res.unwrap_err()); } fn create_testfile(path: &Path, filename: &str) -> File { @@ -60,6 +51,7 @@ fn create_testfile(path: &Path, filename: &str) -> File { File::open(&path).unwrap() } + #[test] /// Same as above but with mask instead of == and also 3 rulesets fn different_rulesets_same_syscall2() { @@ -76,14 +68,19 @@ fn different_rulesets_same_syscall2() { // Add three different rulesets each allowing reads to a different file SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(SystemIO::nothing().allow_file_read(&file1)) - .unwrap() - .enable(SystemIO::nothing().allow_file_read(&file2)) - .unwrap() - .enable(SystemIO::nothing().allow_file_read(&file3)) - .unwrap() + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr() + ).unwrap() + .enable(SystemIO::nothing() + .allow_file_read(&file1) + ).unwrap() + .enable(SystemIO::nothing() + .allow_file_read(&file2) + ).unwrap() + .enable(SystemIO::nothing() + .allow_file_read(&file3) + ).unwrap() .apply_to_current_thread() .unwrap(); diff --git a/tests/sleep.rs b/tests/sleep.rs index fb9d91c..4cc9766 100644 --- a/tests/sleep.rs +++ b/tests/sleep.rs @@ -1,13 +1,13 @@ -use extrasafe::builtins::{danger_zone::Threads, SystemIO}; +use extrasafe::builtins::{SystemIO, danger_zone::Threads}; #[test] #[should_panic] fn insomnia() { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .apply_to_current_thread().unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); } @@ -15,12 +15,12 @@ fn insomnia() { #[test] fn comfy() { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .enable(Threads::nothing().allow_sleep().yes_really()) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .enable(Threads::nothing() + .allow_sleep().yes_really()).unwrap() + .apply_to_current_thread().unwrap(); std::thread::sleep(std::time::Duration::from_millis(1)); } diff --git a/tests/test_ref_ruleset.rs b/tests/test_ref_ruleset.rs index af09176..7879846 100644 --- a/tests/test_ref_ruleset.rs +++ b/tests/test_ref_ruleset.rs @@ -1,11 +1,9 @@ -use extrasafe::builtins::BasicCapabilities; use extrasafe::RuleSet; +use extrasafe::builtins::BasicCapabilities; #[test] /// Test if `RuleSets` can be references. fn ref_ruleset() -> Result<(), extrasafe::ExtraSafeError> { let ruleset: &dyn RuleSet = &BasicCapabilities; - extrasafe::SafetyContext::new() - .enable(ruleset)? - .apply_to_current_thread() + extrasafe::SafetyContext::new().enable(ruleset)?.apply_to_current_thread() } diff --git a/tests/tests_can_fail.rs b/tests/tests_can_fail.rs index 099b383..b4f5872 100644 --- a/tests/tests_can_fail.rs +++ b/tests/tests_can_fail.rs @@ -5,7 +5,8 @@ /// This is also manually tested by commenting out the assert line and checking that the test /// failure propagates to the cli fn seccomp_active_tests_fail() { - let res = extrasafe::SafetyContext::new().apply_to_current_thread(); + let res = extrasafe::SafetyContext::new() + .apply_to_current_thread(); assert!(res.is_ok(), "Extrasafe failed {:?}", res.unwrap_err()); assert!(false, "should fail"); diff --git a/tests/thread_multi.rs b/tests/thread_multi.rs index fbb281f..7b28213 100644 --- a/tests/thread_multi.rs +++ b/tests/thread_multi.rs @@ -16,10 +16,10 @@ fn sync_thread_contexts() { let seccomp_thread = thread::spawn(move || { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .apply_to_all_threads() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .apply_to_all_threads().unwrap(); // setup_done sender1.send(()).unwrap(); diff --git a/tests/thread_single.rs b/tests/thread_single.rs index 7e5d462..504fb71 100644 --- a/tests/thread_single.rs +++ b/tests/thread_single.rs @@ -16,16 +16,17 @@ use std::fs::File; /// threads. This is achieved in this test by blocking IO on one thread and not on another, and /// checking IO can be performed in the other thread after loading the context in the first. fn different_threads_with_different_contexts() { + // These channels will block on send until the receiver has called recv. let (sender1, recv1) = sync_channel::<()>(0); let (sender2, recv2) = sync_channel::<()>(0); let seccomp_thread = thread::spawn(move || { extrasafe::SafetyContext::new() - .enable(SystemIO::nothing().allow_stdout().allow_stderr()) - .unwrap() - .apply_to_current_thread() - .unwrap(); + .enable(SystemIO::nothing() + .allow_stdout() + .allow_stderr()).unwrap() + .apply_to_current_thread().unwrap(); // setup_done sender1.send(()).unwrap(); diff --git a/tests/unsupported_os.rs b/tests/unsupported_os.rs index 0daeb22..3df8bc3 100644 --- a/tests/unsupported_os.rs +++ b/tests/unsupported_os.rs @@ -1,7 +1,8 @@ #[cfg(not(target_os = "linux"))] #[test] fn returns_unsupported_os_error() { - let res = extrasafe::SafetyContext::new().apply_to_all_threads(); + let res = extrasafe::SafetyContext::new() + .apply_to_all_threads(); assert!( res.is_err(), From b2363c8634c3f601a79cd8d3c515e59761592540 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 06:35:23 +0100 Subject: [PATCH 06/25] Simplify this, drop RISC-V, use Qemu in CI --- .github/workflows/build-test.yaml | 22 +++------------------- .idea/.gitignore | 10 ++++++++++ .idea/extrasafe-multiarch-2.iml | 14 ++++++++++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ Cargo.toml | 3 --- Makefile | 10 +--------- build.rs | 12 ------------ src/builtins/basic.rs | 2 +- src/builtins/danger_zone.rs | 8 ++++---- src/builtins/network.rs | 16 ++++++++-------- src/builtins/pipes.rs | 2 +- src/builtins/systemio.rs | 22 +++++++++++----------- src/builtins/time.rs | 2 +- src/error.rs | 2 +- src/lib.rs | 10 +++++----- src/syscalls.rs | 25 ------------------------- 17 files changed, 74 insertions(+), 100 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/extrasafe-multiarch-2.iml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml delete mode 100644 build.rs delete mode 100644 src/syscalls.rs diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index e45305f..babe4a3 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -21,24 +21,6 @@ jobs: test: name: Unit Tests runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - target-triple: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl] - steps: - - name: Install musl lib - if: ${{ contains(matrix.target-triple, 'musl') }} - run: sudo apt-get install musl-tools - - name: Install target triple - run: rustup target install ${{ matrix.target-triple }} - - name: Checkout - uses: actions/checkout@v4 - - name: Run tests - run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} - - check: - name: Check if code compiles - runs-on: ubuntu-latest strategy: fail-fast: false matrix: @@ -55,10 +37,12 @@ jobs: run: sudo apt-get install musl-tools - name: Install target triple run: rustup target install ${{ matrix.target-triple }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 - name: Checkout uses: actions/checkout@v4 - name: Run tests - run: make check-ci TARGET_TRIPLE=${{ matrix.target-triple }} + run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} doc: name: Documentation Check diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..a9d7db9 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/.idea/extrasafe-multiarch-2.iml b/.idea/extrasafe-multiarch-2.iml new file mode 100644 index 0000000..6b3734b --- /dev/null +++ b/.idea/extrasafe-multiarch-2.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..7ed2d6e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index e435f66..8f53544 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,6 @@ categories = ["os::linux-apis"] [features] landlock = ["dep:landlock"] -aarch64 = ["syscalls/aarch64"] -x86_64 = ["syscalls/x86_64"] -riscv64 = ["syscalls/riscv64"] [dependencies] seccompiler = { version = "^0.4", default-features = false } diff --git a/Makefile b/Makefile index 4e1643d..1a1bfe8 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,5 @@ build: - cargo build --feature=landlock - -# Checks if code can be built -check: - cargo check --features=landlock - -check-ci: - cargo check --target=$(TARGET_TRIPLE) --features=landlock - cargo check --target=$(TARGET_TRIPLE) --no-default-features + cargo build --all-features # Run all tests and examples test: diff --git a/build.rs b/build.rs deleted file mode 100644 index 2c3f219..0000000 --- a/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - // prefer method A if both method A and B are selected - if cfg!(feature = "__aarch64") || cfg!(target_arch = "aarch64") { - println!("cargo:rustc-cfg=enabled_arch=\"aarch64\""); - } else if cfg!(feature = "__riscv64") || cfg!(target_arch = "riscv64") { - println!("cargo:rustc-cfg=enabled_arch=\"riscv64\""); - } else if cfg!(feature = "__x86_64") || cfg!(target_arch = "x86_64") { - println!("cargo:rustc-cfg=enabled_arch=\"x86_64\""); - } else { - panic!("No architecture feature enabled and no supported architecture detected. Please enable one of the following features: x86_64, aarch64, riscv64"); - } -} diff --git a/src/builtins/basic.rs b/src/builtins/basic.rs index f8b6b64..e631765 100644 --- a/src/builtins/basic.rs +++ b/src/builtins/basic.rs @@ -42,7 +42,7 @@ impl RuleSet for BasicCapabilities { // Readlink isn't dangerous because you still need to be able to open the file to do // anything with the resolved name. - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::readlink, Sysno::readlinkat, diff --git a/src/builtins/danger_zone.rs b/src/builtins/danger_zone.rs index cdbb6ed..c2f83f4 100644 --- a/src/builtins/danger_zone.rs +++ b/src/builtins/danger_zone.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; -use crate::syscalls::Sysno; +use syscalls::Sysno; use crate::{SeccompRule, RuleSet}; @@ -91,9 +91,9 @@ pub struct ForkAndExec; impl RuleSet for ForkAndExec { fn simple_rules(&self) -> Vec { let mut rules = vec![ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::fork, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::vfork, Sysno::execve, Sysno::execveat, Sysno::wait4, Sysno::waitid, @@ -104,7 +104,7 @@ impl RuleSet for ForkAndExec { // create the pipe if cfg!(target_env = "musl") { rules.extend([ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::pipe, Sysno::pipe2, ]); diff --git a/src/builtins/network.rs b/src/builtins/network.rs index 38edb0f..d6a4d50 100644 --- a/src/builtins/network.rs +++ b/src/builtins/network.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; -use crate::syscalls::Sysno; +use syscalls::Sysno; use super::YesReally; use crate::{SeccompRule, RuleSet}; @@ -11,20 +11,20 @@ use crate::{SeccompRule, RuleSet}; // TODO: add io_uring const NET_IO_SYSCALLS: &[Sysno] = &[ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::epoll_create, Sysno::epoll_create1, Sysno::epoll_ctl, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::epoll_wait, Sysno::epoll_pwait, Sysno::epoll_pwait2, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::select, Sysno::pselect6, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::poll, Sysno::ppoll, Sysno::accept, Sysno::accept4, // used in reqwest::blocking I guess to notify when blocking reads finish? - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::eventfd, Sysno::eventfd2, // Used to set tcp_nodelay @@ -292,11 +292,11 @@ impl Networking { } impl RuleSet for Networking { - fn simple_rules(&self) -> Vec { + fn simple_rules(&self) -> Vec { self.allowed.iter().copied().collect() } - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { self.custom.clone() } diff --git a/src/builtins/pipes.rs b/src/builtins/pipes.rs index 72ca53e..6ca11e3 100644 --- a/src/builtins/pipes.rs +++ b/src/builtins/pipes.rs @@ -9,7 +9,7 @@ pub struct Pipes; impl RuleSet for Pipes { fn simple_rules(&self) -> Vec { vec![ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::pipe, Sysno::pipe2 ] diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index 384a2c3..d7a2ac5 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd; #[cfg(feature = "landlock")] use std::path::{Path, PathBuf}; -use crate::syscalls::Sysno; +use syscalls::Sysno; #[cfg(feature = "landlock")] use crate::LandlockRule; @@ -36,7 +36,7 @@ pub(crate) const IO_WRITE_SYSCALLS: &[Sysno] = &[ Sysno::lseek, ]; pub(crate) const IO_OPEN_SYSCALLS: &[Sysno] = &[ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::open, Sysno::openat, Sysno::openat2 @@ -44,24 +44,24 @@ pub(crate) const IO_OPEN_SYSCALLS: &[Sysno] = &[ pub(crate) const IO_IOCTL_SYSCALLS: &[Sysno] = &[Sysno::ioctl, Sysno::fcntl]; // TODO: may want to separate fd-based and filename-based? pub(crate) const IO_METADATA_SYSCALLS: &[Sysno] = &[ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::stat, Sysno::fstat, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::newfstatat, - #[cfg(any(enabled_arch = "aarch64", enabled_arch = "riscv64"))] + #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))] Sysno::fstatat, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::lstat, Sysno::statx, - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::getdents, Sysno::getdents64, Sysno::getcwd, ]; pub(crate) const IO_CLOSE_SYSCALLS: &[Sysno] = &[Sysno::close, Sysno::close_range]; pub(crate) const IO_UNLINK_SYSCALLS: &[Sysno] = &[ - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] Sysno::unlink, Sysno::unlinkat ]; @@ -161,7 +161,7 @@ impl SystemIO { const WRITECREATE: u64 = O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_EXCL;// | O_TMPFILE; // flags are the second argument for open but the third for openat - #[cfg(enabled_arch = "x86_64")] + #[cfg(target_arch = "x86_64")] { let rule = SeccompRule::new(Sysno::open) .and_condition(seccomp_arg_filter!(arg1 & WRITECREATE == 0)); @@ -282,11 +282,11 @@ impl SystemIO { } impl RuleSet for SystemIO { - fn simple_rules(&self) -> Vec { + fn simple_rules(&self) -> Vec { self.allowed.iter().copied().collect() } - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { self.custom.clone() } diff --git a/src/builtins/time.rs b/src/builtins/time.rs index 3b22ba9..a9b9ec6 100644 --- a/src/builtins/time.rs +++ b/src/builtins/time.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; -use crate::syscalls::Sysno; +use syscalls::Sysno; use crate::{SeccompRule, RuleSet}; diff --git a/src/error.rs b/src/error.rs index 2174767..2c65291 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,7 +17,7 @@ use landlock::PathFdError; pub enum ExtraSafeError { /// Error created when a simple Seccomp rule would override a conditional rule, or when trying to add a /// conditional rule when there's already a simple rule with the same syscall. - ConditionalNoEffectError(crate::syscalls::Sysno, &'static str, &'static str), + ConditionalNoEffectError(syscalls::Sysno, &'static str, &'static str), /// An error from the underlying seccomp library. SeccompError(SeccompilerError), /// No rules were enabled in the SafetyContext. diff --git a/src/lib.rs b/src/lib.rs index 4988d15..b1ef816 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ pub use seccompiler::SeccompCmpOp as SeccompilerComparator; use seccompiler::SeccompAction; -pub mod syscalls; +pub use syscalls; pub mod error; pub use error::*; @@ -180,11 +180,11 @@ struct LabeledSeccompRule(pub &'static str, pub SeccompRule); /// functionality, such as opening files or starting threads. pub trait RuleSet { /// A simple rule is a seccomp rule that just allows the syscall without restriction. - fn simple_rules(&self) -> Vec; + fn simple_rules(&self) -> Vec; /// A conditional rule is a seccomp rule that uses a condition to restrict the syscall, e.g. only /// specific flags as parameters. - fn conditional_rules(&self) -> HashMap> { + fn conditional_rules(&self) -> HashMap> { HashMap::new() } @@ -528,8 +528,8 @@ impl SafetyContext { assert!(result.is_none(), "extrasafe logic error: somehow inserted the same syscall's rules twice"); } - #[cfg(not(target_os = "linux"))] - compile_error!("extrasafe is currently only supported on linux"); + #[cfg(not(all(target_os = "linux", any(target_arch = "aarch64", target_arch = "x86_64"))))] + compile_error!("extrasafe is currently only supported on arm64 and amd64 linux"); let seccompiler_filter = SeccompilerFilter::new( rules_map, diff --git a/src/syscalls.rs b/src/syscalls.rs deleted file mode 100644 index e8e6a71..0000000 --- a/src/syscalls.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Syscalls export -//! This module re-exports syscalls for the target architecture. - -#[cfg(all(feature = "x86_64", feature = "__aarch64"))] -compile_error!("feature \"x86_64\" and feature \"aarch64\" cannot be enabled at the same time!"); -#[cfg(all(feature = "x86_64", feature = "__riscv64"))] -compile_error!("feature \"x86_64\" and feature \"riscv64\" cannot be enabled at the same time!"); -#[cfg(all(feature = "__riscv64", feature = "__aarch64"))] -compile_error!("feature \"riscv64\" and feature \"aarch64\" cannot be enabled at the same time!"); - -#[cfg(all( - any(feature = "x86_64", target_arch = "x86_64"), - not(any(feature = "aarch64", feature = "riscv64")) -))] -pub use syscalls::x86_64::*; -#[cfg(all( - any(feature = "aarch64", target_arch = "aarch64"), - not(any(feature = "x86_64", feature = "riscv64")) -))] -pub use syscalls::aarch64::*; -#[cfg(all( - any(feature = "riscv64", target_arch = "riscv64"), - not(any(feature = "x86_64", feature = "aarch64")) -))] -pub use syscalls::riscv64::*; From 629a935cefeea28981bbe749d4a2e3e3489d4a65 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 06:36:12 +0100 Subject: [PATCH 07/25] Remove RustRover files --- .idea/.gitignore | 10 ---------- .idea/extrasafe-multiarch-2.iml | 14 -------------- .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ 4 files changed, 38 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/extrasafe-multiarch-2.iml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index a9d7db9..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# GitHub Copilot persisted chat sessions -/copilot/chatSessions diff --git a/.idea/extrasafe-multiarch-2.iml b/.idea/extrasafe-multiarch-2.iml deleted file mode 100644 index 6b3734b..0000000 --- a/.idea/extrasafe-multiarch-2.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 7ed2d6e..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 67c0db7fcb8983c425793f6e496445375e104b46 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 06:37:03 +0100 Subject: [PATCH 08/25] Revert tests change --- tests/arg_comparisons.rs | 2 +- tests/sysno.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/arg_comparisons.rs b/tests/arg_comparisons.rs index c47f1cc..f233c22 100644 --- a/tests/arg_comparisons.rs +++ b/tests/arg_comparisons.rs @@ -3,7 +3,7 @@ use extrasafe::*; use builtins::SystemIO; -use extrasafe::syscalls::Sysno; +use syscalls::Sysno; use std::collections::HashMap; diff --git a/tests/sysno.rs b/tests/sysno.rs index 3005f58..78d6a86 100644 --- a/tests/sysno.rs +++ b/tests/sysno.rs @@ -1,5 +1,5 @@ use extrasafe::SafetyContext; -use extrasafe::syscalls::Sysno; +use syscalls::Sysno; #[test] #[should_panic] From 8e262e0418b521ed646803d2dd283d7d22d2e39f Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 06:37:56 +0100 Subject: [PATCH 09/25] Completely revert makefile --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1a1bfe8..99b2bd9 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,12 @@ test: # Run all tests with and without all features test-ci: - cargo test --target=$(TARGET_TRIPLE) --tests --examples --feature=landlock + cargo test --target=$(TARGET_TRIPLE) --tests --examples --all-features cargo test --target=$(TARGET_TRIPLE) --tests --examples --no-default-features # Run clippy lint: - cargo clippy --no-deps --all-targets --feature=landlock -- -W clippy::pedantic \ + cargo clippy --no-deps --all-targets --all-features -- -W clippy::pedantic \ -A clippy::let-unit-value \ -A clippy::wildcard-imports \ -A clippy::module-name-repetitions \ @@ -24,8 +24,8 @@ doc: # Compute test coverage for CI with llvm-cov coverage-ci: - cargo llvm-cov --tests --examples --all-targets --feature=landlock --workspace --lcov --output-path lcov.info + cargo llvm-cov --tests --examples --all-targets --all-features --workspace --lcov --output-path lcov.info # Compute test coverage with HTML output coverage: - cargo llvm-cov --tests --examples --all-targets --feature=landlock --workspace --html + cargo llvm-cov --tests --examples --all-targets --all-features --workspace --html From b9dec5e198a54b1eb906763c68e041f65f15f40a Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 07:15:59 +0100 Subject: [PATCH 10/25] Try to improve OpenSSL handling --- .github/workflows/build-test.yaml | 6 ++++-- Cargo.toml | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index babe4a3..194553b 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -29,15 +29,17 @@ jobs: - x86_64-unknown-linux-musl - aarch64-unknown-linux-gnu - aarch64-unknown-linux-musl - - riscv64gc-unknown-linux-gnu - - riscv64gc-unknown-linux-musl steps: - name: Install musl lib if: ${{ contains(matrix.target-triple, 'musl') }} run: sudo apt-get install musl-tools + - name: Install cross-compilation dependencies + if: ${{ contains(matrix.target-triple, 'aarch64') }} + run: apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - name: Install target triple run: rustup target install ${{ matrix.target-triple }} - name: Set up QEMU + if: ${{ contains(matrix.target-triple, 'aarch64') }} uses: docker/setup-qemu-action@v3 - name: Checkout uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 8f53544..4762132 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,5 +32,9 @@ rusqlite = "^0.26" [target.'cfg(target_env = "musl")'.dev-dependencies] reqwest = { version = "^0.11", default-features = false, features = ["rustls-tls"] } -[target.'cfg(not(target_env = "musl"))'.dev-dependencies] +[target.'cfg(all(not(target_env = "musl"), target_arch = "aarch64"))'.dev-dependencies] +reqwest = { version = "^0.11" } +openssl-sys = { version = "0.9.101", features = ["openssl-src"] } + +[target.'cfg(not(any(target_env = "musl", target_arch = "aarch64")))'.dev-dependencies] reqwest = { version = "^0.11" } From ec4457d37d6af7c22e95e5e9c6bbf8567a0ec45d Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 07:38:46 +0100 Subject: [PATCH 11/25] sudo --- .github/workflows/build-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 194553b..b1fd4db 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -35,7 +35,7 @@ jobs: run: sudo apt-get install musl-tools - name: Install cross-compilation dependencies if: ${{ contains(matrix.target-triple, 'aarch64') }} - run: apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu + run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - name: Install target triple run: rustup target install ${{ matrix.target-triple }} - name: Set up QEMU From 20da728efce0100fe455205af68dfb78098da544 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 07:41:37 +0100 Subject: [PATCH 12/25] TLS vendored --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4762132..3ee21a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,7 @@ rusqlite = "^0.26" reqwest = { version = "^0.11", default-features = false, features = ["rustls-tls"] } [target.'cfg(all(not(target_env = "musl"), target_arch = "aarch64"))'.dev-dependencies] -reqwest = { version = "^0.11" } -openssl-sys = { version = "0.9.101", features = ["openssl-src"] } +reqwest = { version = "^0.11", features = ["native-tls-vendored"] } [target.'cfg(not(any(target_env = "musl", target_arch = "aarch64")))'.dev-dependencies] reqwest = { version = "^0.11" } From 716e833edeea49333df44409128dde3581bfdd55 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 07:43:10 +0100 Subject: [PATCH 13/25] musl fix --- .github/workflows/build-test.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index b1fd4db..c526b7a 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -33,9 +33,12 @@ jobs: - name: Install musl lib if: ${{ contains(matrix.target-triple, 'musl') }} run: sudo apt-get install musl-tools - - name: Install cross-compilation dependencies - if: ${{ contains(matrix.target-triple, 'aarch64') }} + - name: Install cross-compilation dependencies (GNU) + if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu + - name: Install cross-compilation dependencies (musl) + if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'musl') }} + run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-musl - name: Install target triple run: rustup target install ${{ matrix.target-triple }} - name: Set up QEMU From a195ce0cf7aa53493134b436d8bd5c08f314f460 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 08:59:06 +0100 Subject: [PATCH 14/25] Some slight improvements --- .github/workflows/build-test.yaml | 3 --- src/builtins/systemio.rs | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index c526b7a..695d0fa 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,9 +36,6 @@ jobs: - name: Install cross-compilation dependencies (GNU) if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - - name: Install cross-compilation dependencies (musl) - if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'musl') }} - run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-musl - name: Install target triple run: rustup target install ${{ matrix.target-triple }} - name: Set up QEMU diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index d7a2ac5..f85cf0b 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -356,6 +356,8 @@ impl SystemIO { self.insert_flags(path, new_flags); // allow relevant syscalls as well + // creat only exists on x86-64, aarch64 uses O_CREAT with open + #[cfg(target_arch = "x86_64")] self.allowed.extend(&[Sysno::creat]); self.allow_open().yes_really() } @@ -391,7 +393,11 @@ impl SystemIO { self.insert_flags(path, new_flags); // allow relevant syscalls as well - self.allowed.extend(&[Sysno::unlink, Sysno::unlinkat]); + self.allowed.extend(&[ + #[cfg(target_arch = "x86_64")] + Sysno::unlink, + Sysno::unlinkat + ]); self } @@ -410,7 +416,11 @@ impl SystemIO { // allow relevant syscalls as well // unlinkat may be be used to remove directories as well so we include it here, since files // will be protected by landlock anyway. - self.allowed.extend(&[Sysno::rmdir, Sysno::unlinkat]); + self.allowed.extend(&[ + #[cfg(target_arch = "x86_64")] + Sysno::rmdir, + Sysno::unlinkat + ]); self } } From 68e58c5c073747caf56917dce9b3cf98024045ed Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 18 Mar 2024 09:02:38 +0100 Subject: [PATCH 15/25] Last build fix --- src/builtins/systemio.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/builtins/systemio.rs b/src/builtins/systemio.rs index f85cf0b..be9a65f 100644 --- a/src/builtins/systemio.rs +++ b/src/builtins/systemio.rs @@ -382,7 +382,11 @@ impl SystemIO { self.insert_flags(path, new_flags); // allow relevant syscalls as well - self.allowed.extend(&[Sysno::mkdir, Sysno::mkdirat]); + self.allowed.extend(&[ + #[cfg(target_arch = "x86_64")] + Sysno::mkdir, + Sysno::mkdirat + ]); self } From 261ccaa3e30c643544bf65c04716cfa715fe24fc Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 20 Mar 2024 20:14:48 +0100 Subject: [PATCH 16/25] Check if this works --- .github/workflows/build-test.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 695d0fa..018bd9f 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,6 +36,9 @@ jobs: - name: Install cross-compilation dependencies (GNU) if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu + - name: Install arm64 linker + if: ${{ contains(matrix.target-triple, 'aarch64') }} + run: sudo apt-get install binutils-aarch64-linux-gnu - name: Install target triple run: rustup target install ${{ matrix.target-triple }} - name: Set up QEMU @@ -43,8 +46,12 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Checkout uses: actions/checkout@v4 - - name: Run tests + - name: Run tests (x86_64) + if: ${{ contains(matrix.target-triple, 'x86_64') }} run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} + - name: Run tests (arm64) + if: ${{ contains(matrix.target-triple, 'aarch64') }} + run: RUSTFLAGS="-C link-arg=-fuse-ld=rust-lld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} doc: name: Documentation Check From b780975e4517250a3f656da7f69f715214d89669 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 20 Mar 2024 20:17:27 +0100 Subject: [PATCH 17/25] Try lld --- .github/workflows/build-test.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 018bd9f..c0ebf4c 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,6 +36,9 @@ jobs: - name: Install cross-compilation dependencies (GNU) if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu + - name: Install cross-compilation dependencies (muskl) + if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} + run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - name: Install arm64 linker if: ${{ contains(matrix.target-triple, 'aarch64') }} run: sudo apt-get install binutils-aarch64-linux-gnu @@ -51,7 +54,7 @@ jobs: run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} - name: Run tests (arm64) if: ${{ contains(matrix.target-triple, 'aarch64') }} - run: RUSTFLAGS="-C link-arg=-fuse-ld=rust-lld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} + run: RUSTFLAGS="-C link-arg=-fuse-ld=lld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} doc: name: Documentation Check From 41b4304538e15fae593954a550c32833833b30e3 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 20 Mar 2024 20:21:00 +0100 Subject: [PATCH 18/25] Update build-test.yaml --- .github/workflows/build-test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index c0ebf4c..7136ace 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -54,7 +54,7 @@ jobs: run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} - name: Run tests (arm64) if: ${{ contains(matrix.target-triple, 'aarch64') }} - run: RUSTFLAGS="-C link-arg=-fuse-ld=lld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} + run: RUSTFLAGS="-C link-arg=-fuse-ld=/usr/aarch64-linux-gnu/bin/ld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} doc: name: Documentation Check From be61f527088397357a0cf469917850f6a1ad0609 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 20 Mar 2024 20:26:23 +0100 Subject: [PATCH 19/25] Try cross-rs --- .github/workflows/build-test.yaml | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 7136ace..c1ffe43 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -30,31 +30,14 @@ jobs: - aarch64-unknown-linux-gnu - aarch64-unknown-linux-musl steps: - - name: Install musl lib - if: ${{ contains(matrix.target-triple, 'musl') }} - run: sudo apt-get install musl-tools - - name: Install cross-compilation dependencies (GNU) - if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} - run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - - name: Install cross-compilation dependencies (muskl) - if: ${{ contains(matrix.target-triple, 'aarch64') && contains(matrix.target-triple, 'gnu') }} - run: sudo apt-get install libc6-dev-arm64-cross gcc-aarch64-linux-gnu - - name: Install arm64 linker - if: ${{ contains(matrix.target-triple, 'aarch64') }} - run: sudo apt-get install binutils-aarch64-linux-gnu - - name: Install target triple - run: rustup target install ${{ matrix.target-triple }} - - name: Set up QEMU - if: ${{ contains(matrix.target-triple, 'aarch64') }} - uses: docker/setup-qemu-action@v3 + - name: Install cross tools + run: cargo install cross --git https://github.com/cross-rs/cross - name: Checkout uses: actions/checkout@v4 - - name: Run tests (x86_64) - if: ${{ contains(matrix.target-triple, 'x86_64') }} - run: make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} - - name: Run tests (arm64) - if: ${{ contains(matrix.target-triple, 'aarch64') }} - run: RUSTFLAGS="-C link-arg=-fuse-ld=/usr/aarch64-linux-gnu/bin/ld" make test-ci TARGET_TRIPLE=${{ matrix.target-triple }} + - name: Run tests + run: | + cross test --target${{ matrix.target-triple }} --tests --examples --all-features + cross test --target${{ matrix.target-triple }} --tests --examples --no-default-features doc: name: Documentation Check From d53fb3edae12befea36660c679f675b6422953bb Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 20 Mar 2024 20:28:36 +0100 Subject: [PATCH 20/25] Space --- .github/workflows/build-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index c1ffe43..8f886e7 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -36,8 +36,8 @@ jobs: uses: actions/checkout@v4 - name: Run tests run: | - cross test --target${{ matrix.target-triple }} --tests --examples --all-features - cross test --target${{ matrix.target-triple }} --tests --examples --no-default-features + cross test --target ${{ matrix.target-triple }} --tests --examples --all-features + cross test --target ${{ matrix.target-triple }} --tests --examples --no-default-features doc: name: Documentation Check From 9941e00ca0b4b402216c2755d27f3c4e03921a97 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Fri, 5 Apr 2024 10:59:49 +0200 Subject: [PATCH 21/25] Cross config --- Cross.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Cross.toml diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..5805bd0 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,5 @@ +[build] +pre-build = [ + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH" +] From c0516a129e8286135076c1b0b7c3488ff80ca7e2 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Fri, 5 Apr 2024 11:03:04 +0200 Subject: [PATCH 22/25] Improve cross config --- Cross.toml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Cross.toml b/Cross.toml index 5805bd0..56d6114 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,5 +1,11 @@ -[build] +[target.aarch64-unknown-linux-gnu] pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", - "apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH" + "apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH libsqlite3-dev:$CROSS_DEB_ARCH" +] + +[target.x86_64-unknown-linux-gnu] +pre-build = [ + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH libsqlite3-dev:$CROSS_DEB_ARCH" ] From 031ab56f721567f2310320fa06f933651a8d51bb Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Fri, 5 Apr 2024 11:07:53 +0200 Subject: [PATCH 23/25] Fix isolate_sys on arm64 --- src/isolate/isolate_sys.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/isolate/isolate_sys.rs b/src/isolate/isolate_sys.rs index e7547b8..1f69ac8 100644 --- a/src/isolate/isolate_sys.rs +++ b/src/isolate/isolate_sys.rs @@ -18,6 +18,7 @@ use std::ffi::CString; use super::IsolateError; use std::io::Write; use std::os::fd::FromRawFd; +use std::os::raw::c_char; use std::collections::HashMap; @@ -142,7 +143,7 @@ pub fn make_tempdir(isolate_name: &str) -> PathBuf { let template_str = format!("/tmp/{}.XXXXXX\0", isolate_name); let mut dir_buf: Vec = template_str.clone().into_bytes(); - let dir_ptr: *mut i8 = dir_buf.as_mut_ptr().cast::(); + let dir_ptr: *mut c_char = dir_buf.as_mut_ptr().cast::(); let ret = unsafe { libc::mkdtemp(dir_ptr) }; fail_null!(ret, "failed to create temporary directory after clone"); From aa9112f274a00c47bdaa15556f5590d9e3fc48fd Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Fri, 5 Apr 2024 11:09:11 +0200 Subject: [PATCH 24/25] Another musl fixup --- Cross.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Cross.toml b/Cross.toml index 56d6114..11d416f 100644 --- a/Cross.toml +++ b/Cross.toml @@ -9,3 +9,13 @@ pre-build = [ "dpkg --add-architecture $CROSS_DEB_ARCH", "apt-get update && apt-get install --assume-yes libssl-dev:$CROSS_DEB_ARCH libsqlite3-dev:$CROSS_DEB_ARCH" ] + +[target.x86_64-unknown-linux-musl] +pre-build = [ + "apt-get update && apt-get install --assume-yes libsqlite3-dev" +] + +[target.aarch64-unknown-linux-musl] +pre-build = [ + "apt-get update && apt-get install --assume-yes libsqlite3-dev" +] From 50c84b353ad7b80dc9ea0765a7ea95fb55681ccf Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Fri, 5 Apr 2024 11:12:07 +0200 Subject: [PATCH 25/25] Another update --- Cross.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cross.toml b/Cross.toml index 11d416f..2886fff 100644 --- a/Cross.toml +++ b/Cross.toml @@ -12,10 +12,12 @@ pre-build = [ [target.x86_64-unknown-linux-musl] pre-build = [ - "apt-get update && apt-get install --assume-yes libsqlite3-dev" + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update && apt-get install --assume-yes libsqlite3-dev:$CROSS_DEB_ARCH" ] [target.aarch64-unknown-linux-musl] pre-build = [ - "apt-get update && apt-get install --assume-yes libsqlite3-dev" + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update && apt-get install --assume-yes libsqlite3-dev:$CROSS_DEB_ARCH" ]