diff --git a/Cargo.toml b/Cargo.toml index fcac6bc348..9c73b2ad5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -229,9 +229,6 @@ tube_transporter = { path = "tube_transporter" } winapi = "*" win_util = { path = "win_util"} -[build-dependencies] -cc = "*" - [dev-dependencies] base = "*" diff --git a/build.rs b/build.rs deleted file mode 100644 index 1cb33b633e..0000000000 --- a/build.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2022 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#[cfg(unix)] -use std::env; -use std::fs; -use std::path::Path; -#[cfg(unix)] -use std::path::PathBuf; -use std::process::Command; - -fn generate_preprocessed(minijail_dir: &Path, out_dir: &Path) { - let env_cc = cc::Build::new() - .get_compiler() - .path() - .as_os_str() - .to_owned(); - - Command::new(minijail_dir.join("gen_constants.sh")) - .env("CC", &env_cc) - .env("SRC", &minijail_dir) - .arg(out_dir.join("libconstants.gen.c")) - .spawn() - .unwrap() - .wait() - .expect("Generate kernel constant table failed"); - - Command::new(minijail_dir.join("gen_syscalls.sh")) - .env("CC", &env_cc) - .env("SRC", &minijail_dir) - .arg(out_dir.join("libsyscalls.gen.c")) - .spawn() - .unwrap() - .wait() - .expect("Generate syscall table failed"); -} - -fn generate_llvm_ir(minijail_dir: &Path, out_dir: &Path, target: &str) { - Command::new("clang") - .arg("-target") - .arg(target) - .arg("-S") - .arg("-emit-llvm") - .arg("-I") - .arg(minijail_dir) - .arg(out_dir.join("libconstants.gen.c")) - .arg(out_dir.join("libsyscalls.gen.c")) - .current_dir(&out_dir) - .spawn() - .unwrap() - .wait() - .expect("Convert kernel constants and syscalls to llvm ir failed"); -} - -fn generate_constants_json(minijail_dir: &Path, out_dir: &Path) { - Command::new(minijail_dir.join("tools/generate_constants_json.py")) - .arg("--output") - .arg(out_dir.join("constants.json")) - .arg(out_dir.join("libconstants.gen.ll")) - .arg(out_dir.join("libsyscalls.gen.ll")) - .spawn() - .unwrap() - .wait() - .expect("Generate constants.json failed"); -} - -fn rewrite_policies(seccomp_policy_path: &Path, rewrote_policy_folder: &Path) { - for entry in fs::read_dir(seccomp_policy_path).unwrap() { - let policy_file = entry.unwrap(); - let policy_file_content = fs::read_to_string(policy_file.path()).unwrap(); - let policy_file_content_rewrote = - policy_file_content.replace("/usr/share/policy/crosvm", "."); - fs::write( - rewrote_policy_folder.join(policy_file.file_name()), - policy_file_content_rewrote, - ) - .unwrap(); - } -} - -fn compile_policies(out_dir: &Path, rewrote_policy_folder: &Path, minijail_dir: &Path) { - let compiled_policy_folder = out_dir.join("policy_output"); - fs::create_dir_all(&compiled_policy_folder).unwrap(); - let mut include_all_bytes = String::from("std::collections::HashMap::from([\n"); - for entry in fs::read_dir(&rewrote_policy_folder).unwrap() { - let policy_file = entry.unwrap(); - if policy_file.path().extension().unwrap() == "policy" { - let output_file_path = compiled_policy_folder.join( - policy_file - .path() - .with_extension("bpf") - .file_name() - .unwrap(), - ); - Command::new(minijail_dir.join("tools/compile_seccomp_policy.py")) - .arg("--arch-json") - .arg(out_dir.join("constants.json")) - .arg("--default-action") - .arg("trap") - .arg(policy_file.path()) - .arg(&output_file_path) - .spawn() - .unwrap() - .wait() - .expect("Compile bpf failed"); - let s = format!( - r#"("{}", include_bytes!("{}").to_vec()),"#, - policy_file.path().file_stem().unwrap().to_str().unwrap(), - output_file_path.to_str().unwrap() - ); - include_all_bytes += s.as_str(); - } - } - include_all_bytes += "])"; - fs::write(out_dir.join("bpf_includes.in"), include_all_bytes).unwrap(); -} - -#[cfg(unix)] -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=seccomp"); - - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let minijail_dir = if let Ok(minijail_dir_env) = env::var("MINIJAIL_DIR") { - PathBuf::from(minijail_dir_env) - } else { - src_dir.join("third_party/minijail") - }; - - let target = env::var("TARGET").unwrap(); - - generate_preprocessed(&minijail_dir, &out_dir); - generate_llvm_ir(&minijail_dir, &out_dir, &target); - generate_constants_json(&minijail_dir, &out_dir); - - // check policies exist for target architecuture - let seccomp_arch_name = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() { - "armv7" => "arm".to_owned(), - x => x.to_owned(), - }; - let seccomp_policy_path = src_dir.join("seccomp").join(&seccomp_arch_name); - assert!( - seccomp_policy_path.is_dir(), - "Seccomp policy dir doesn't exist" - ); - - let rewrote_policy_folder = out_dir.join("policy_input"); - fs::create_dir_all(&rewrote_policy_folder).unwrap(); - rewrite_policies(&seccomp_policy_path, &rewrote_policy_folder); - compile_policies(&out_dir, &rewrote_policy_folder, &minijail_dir); -} - -#[cfg(not(unix))] -fn main() { - println!("cargo:rerun-if-changed=build.rs"); -} diff --git a/docs/book/src/building_crosvm.md b/docs/book/src/building_crosvm.md index 8ee0d95554..bca19b3ebd 100644 --- a/docs/book/src/building_crosvm.md +++ b/docs/book/src/building_crosvm.md @@ -172,13 +172,19 @@ The `--quick` variant will skip some slower checks, like building for other plat ## Known issues +- By default, crosvm is running devices in sandboxed mode, which requires seccomp policy files to be + set up. For local testing it is often easier to `--disable-sandbox` to run everything in a single + process. - If your Linux header files are too old, you may find minijail rejecting seccomp filters for - containing unknown syscalls. You can try removing the offending lines from the filter file and - recompile or add `--seccomp-log-failures` to the crosvm command line to turn these into warnings. - Using this option also requires you to specify path to seccomp policiy source files with - `--seccomp-policy-dir` and adhere to (or modify) the hardcoded absolute include paths in them. - Note that this option will also stop minijail from killing processes that violate the seccomp - rule, making the sandboxing much less aggressive. + containing unknown syscalls. You can try removing the offending lines from the filter file, or add + `--seccomp-log-failures` to the crosvm command line to turn these into warnings. Note that this + option will also stop minijail from killing processes that violate the seccomp rule, making the + sandboxing much less aggressive. +- Seccomp policy files have hardcoded absolute paths. You can either fix up the paths locally, or + set up an awesome hacky symlink: + `sudo mkdir /usr/share/policy && sudo ln -s /path/to/crosvm/seccomp/x86_64 /usr/share/policy/crosvm`. + We'll eventually build the precompiled policies + [into the crosvm binary](http://crbug.com/1052126). - Devices can't be jailed if `/var/empty` doesn't exist. `sudo mkdir -p /var/empty` to work around this for now. - You need read/write permissions for `/dev/kvm` to run tests or other crosvm instances. Usually diff --git a/src/crosvm/config.rs b/src/crosvm/config.rs index 5165d47c86..d1c6c1635b 100644 --- a/src/crosvm/config.rs +++ b/src/crosvm/config.rs @@ -66,6 +66,7 @@ cfg_if::cfg_if! { static KVM_PATH: &str = "/dev/kvm"; static VHOST_NET_PATH: &str = "/dev/vhost-net"; + static SECCOMP_POLICY_DIR: &str = "/usr/share/policy/crosvm"; } else if #[cfg(windows)] { use base::{Event, Tube}; @@ -525,12 +526,18 @@ fn jail_config_default_pivot_root() -> PathBuf { PathBuf::from(option_env!("DEFAULT_PIVOT_ROOT").unwrap_or("/var/empty")) } +#[cfg(unix)] +fn jail_config_default_seccomp_policy_dir() -> Option { + Some(PathBuf::from(SECCOMP_POLICY_DIR)) +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, serde_keyvalue::FromKeyValues)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub struct JailConfig { #[serde(default = "jail_config_default_pivot_root")] pub pivot_root: PathBuf, #[cfg(unix)] + #[serde(default = "jail_config_default_seccomp_policy_dir")] pub seccomp_policy_dir: Option, #[serde(default)] pub seccomp_log_failures: bool, @@ -541,7 +548,7 @@ impl Default for JailConfig { JailConfig { pivot_root: jail_config_default_pivot_root(), #[cfg(unix)] - seccomp_policy_dir: None, + seccomp_policy_dir: jail_config_default_seccomp_policy_dir(), seccomp_log_failures: false, } } @@ -2189,7 +2196,7 @@ mod tests { JailConfig { pivot_root: jail_config_default_pivot_root(), #[cfg(unix)] - seccomp_policy_dir: None, + seccomp_policy_dir: jail_config_default_seccomp_policy_dir(), seccomp_log_failures: false, } ); diff --git a/src/crosvm/sys/unix/jail_helpers.rs b/src/crosvm/sys/unix/jail_helpers.rs index 26fd085090..fe884c9d0d 100644 --- a/src/crosvm/sys/unix/jail_helpers.rs +++ b/src/crosvm/sys/unix/jail_helpers.rs @@ -13,18 +13,10 @@ use libc::c_ulong; use libc::gid_t; use libc::uid_t; use minijail::Minijail; -use once_cell::sync::Lazy; use crate::crosvm::config::JailConfig; -static EMBEDDED_BPFS: Lazy>> = Lazy::new(|| { - if cfg!(unix) { - include!(concat!(env!("OUT_DIR"), "/bpf_includes.in")) - } else { - std::collections::HashMap::<&str, Vec>::new() - } -}); - +#[allow(dead_code)] pub(super) struct SandboxConfig<'a> { pub(super) limit_caps: bool, pub(super) log_failures: bool, @@ -75,55 +67,27 @@ pub(super) fn create_base_minijail( // Don't allow the device to gain new privileges. j.no_new_privs(); - if let Some(seccomp_policy_path) = config.seccomp_policy_path { - // By default we'll prioritize using the pre-compiled .bpf over the - // .policy file (the .bpf is expected to be compiled using "trap" as the - // failure behavior instead of the default "kill" behavior) when a policy - // path is supplied in the command line arugments. Otherwise the built-in - // pre-compiled policies will be used. - // Refer to the code comment for the "seccomp-log-failures" - // command-line parameter for an explanation about why the |log_failures| - // flag forces the use of .policy files (and the build-time alternative to - // this run-time flag). - let bpf_policy_file = seccomp_policy_path.with_extension("bpf"); - if bpf_policy_file.exists() && !config.log_failures { - j.parse_seccomp_program(&bpf_policy_file).with_context(|| { - format!( - "failed to parse precompiled seccomp policy: {}", - bpf_policy_file.display() - ) - })?; - } else { - // Use TSYNC only for the side effect of it using SECCOMP_RET_TRAP, - // which will correctly kill the entire device process if a worker - // thread commits a seccomp violation. - j.set_seccomp_filter_tsync(); - if config.log_failures { - j.log_seccomp_filter_failures(); - } - let bpf_policy_file = seccomp_policy_path.with_extension("policy"); - j.parse_seccomp_filters(&bpf_policy_file).with_context(|| { - format!( - "failed to parse seccomp policy: {}", - bpf_policy_file.display() - ) - })?; - } + // By default we'll prioritize using the pre-compiled .bpf over the .policy + // file (the .bpf is expected to be compiled using "trap" as the failure + // behavior instead of the default "kill" behavior). + // Refer to the code comment for the "seccomp-log-failures" + // command-line parameter for an explanation about why the |log_failures| + // flag forces the use of .policy files (and the build-time alternative to + // this run-time flag). + let bpf_policy_file = config.seccomp_policy_path.unwrap().with_extension("bpf"); + if bpf_policy_file.exists() && !config.log_failures { + j.parse_seccomp_program(&bpf_policy_file) + .context("failed to parse precompiled seccomp policy")?; } else { - let bpf_program = EMBEDDED_BPFS - .get(&config.seccomp_policy_name) - .with_context(|| { - format!( - "failed to find embedded seccomp policy: {}", - &config.seccomp_policy_name - ) - })?; - j.parse_seccomp_bytes(bpf_program).with_context(|| { - format!( - "failed to parse embedded seccomp policy: {}", - &config.seccomp_policy_name - ) - })?; + // Use TSYNC only for the side effect of it using SECCOMP_RET_TRAP, + // which will correctly kill the entire device process if a worker + // thread commits a seccomp violation. + j.set_seccomp_filter_tsync(); + if config.log_failures { + j.log_seccomp_filter_failures(); + } + j.parse_seccomp_filters(&config.seccomp_policy_path.unwrap().with_extension("policy")) + .context("failed to parse seccomp policy")?; } j.use_seccomp_filter(); // Don't do init setup.