Skip to content

Commit c61ae08

Browse files
roypatzulinx86
authored andcommitted
Merge vmm build script into firecracker build script
The build script invokes cargo, which breaks tooling such as miri when attempting to run it on the vmm crate. With us currently moving all other crates into a single library crate, this is starting to become annoying. This also moves the code that reads the compiled seccomp config and includes it into the firecracker binary. Outside the firecracker binary crate, the code in question was only used in unittests. Unittests that used the production filters now use empty filters. This is okay because we run our integration test suite with the production filters, and the filters are out of scope for unittests anyway. With moving the code to a binary crate, rustc's dead code analysis kicked in, so some error variants are removed. Signed-off-by: Patrick Roy <[email protected]>
1 parent c315bb1 commit c61ae08

File tree

10 files changed

+173
-167
lines changed

10 files changed

+173
-167
lines changed

build.rs

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,147 @@
11
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
// this build script is called on en every `devtool build`,
5-
// embedding the FIRECRACKER_VERSION directly in the resulting binary
4+
use std::path::{Path, PathBuf};
5+
use std::process::Command;
6+
use std::{env, fs};
7+
8+
const ADVANCED_BINARY_FILTER_FILE_NAME: &str = "seccomp_filter.bpf";
9+
10+
const JSON_DIR: &str = "../../resources/seccomp";
11+
const SECCOMPILER_BUILD_DIR: &str = "../../build/seccompiler";
12+
const SECCOMPILER_SRC_DIR: &str = "../seccompiler/src";
13+
14+
// This script is run on every modification in the target-specific JSON file in `resources/seccomp`.
15+
// It compiles the JSON seccomp policies into a serializable BPF format, using seccompiler-bin.
16+
// The generated binary code will get included in Firecracker's code, at compile-time.
617
fn main() {
18+
// this build script is called on every `devtool build`,
19+
// embedding the FIRECRACKER_VERSION directly in the resulting binary
720
let firecracker_version = env!("CARGO_PKG_VERSION").to_string();
821
println!(
922
"cargo:rustc-env=FIRECRACKER_VERSION={}",
1023
firecracker_version
1124
);
25+
26+
// Only compile the seccomp filters for the firecracker binary - otherwise
27+
// we'll get stuck in an infinite loop as seccompiler-bin (which is invoked below)
28+
// also executes this build script to set the firecracker version env variable.
29+
if let Ok(package) = std::env::var("CARGO_MANIFEST_DIR") {
30+
if !package.ends_with("firecracker") {
31+
return;
32+
}
33+
}
34+
35+
cpuid();
36+
37+
let target = env::var("TARGET").expect("Missing target.");
38+
let out_dir = env::var("OUT_DIR").expect("Missing build-level OUT_DIR.");
39+
40+
// Path to the JSON seccomp policy.
41+
let mut json_path = PathBuf::from(JSON_DIR);
42+
json_path.push(format!("{}.json", target));
43+
44+
// If the current target doesn't have a default filter, use a default, empty filter.
45+
// This is to make sure that Firecracker builds even with libc toolchains for which we don't
46+
// provide a default filter. For example, GNU libc.
47+
if !json_path.exists() {
48+
json_path.pop();
49+
json_path.push("unimplemented.json");
50+
51+
println!(
52+
"cargo:warning=No default seccomp policy for target: {}. Defaulting to \
53+
`resources/seccomp/unimplemented.json`.",
54+
target
55+
);
56+
}
57+
58+
// Retrigger the build script if the JSON file has changed.
59+
let json_path = json_path.to_str().expect("Invalid bytes");
60+
println!("cargo:rerun-if-changed={}", json_path);
61+
62+
// Also retrigger the build script on any seccompiler source code change.
63+
register_seccompiler_src_watchlist(Path::new(SECCOMPILER_SRC_DIR));
64+
65+
// Run seccompiler-bin, getting the default, advanced filter.
66+
let mut bpf_out_path = PathBuf::from(&out_dir);
67+
bpf_out_path.push(ADVANCED_BINARY_FILTER_FILE_NAME);
68+
run_seccompiler_bin(json_path, bpf_out_path.to_str().expect("Invalid bytes."));
69+
}
70+
71+
// Run seccompiler with the given arguments.
72+
fn run_seccompiler_bin(json_path: &str, out_path: &str) {
73+
// We have a global `target` directive in our .cargo/config file specifying x86_64 architecture.
74+
// However, seccompiler-bin has to be compiled for the host architecture. Without this, cargo
75+
// would produce a x86_64 binary on aarch64 host, causing this compilation step to fail as such
76+
// a binary would not be executable.
77+
let host_arch = env::var("HOST").expect("Could not determine compilation host");
78+
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("Missing target arch.");
79+
80+
// Command for running seccompiler-bin
81+
let mut command = Command::new("cargo");
82+
command.args([
83+
"run",
84+
"-p",
85+
"seccompiler",
86+
"--verbose",
87+
"--target",
88+
&host_arch,
89+
// We need to specify a separate build directory for seccompiler-bin. Otherwise, cargo will
90+
// deadlock waiting to acquire a lock on the build folder that the parent cargo process is
91+
// holding.
92+
"--target-dir",
93+
SECCOMPILER_BUILD_DIR,
94+
"--",
95+
"--input-file",
96+
json_path,
97+
"--target-arch",
98+
&target_arch,
99+
"--output-file",
100+
out_path,
101+
]);
102+
103+
match command.output() {
104+
Err(error) => panic!("\nSeccompiler-bin error: {:?}\n", error),
105+
Ok(result) if !result.status.success() => {
106+
panic!(
107+
"\nSeccompiler-bin returned non-zero exit code:\nstderr: {}\nstdout: {}\n",
108+
String::from_utf8(result.stderr).unwrap(),
109+
String::from_utf8(result.stdout).unwrap(),
110+
);
111+
}
112+
Ok(_) => {}
113+
}
114+
}
115+
116+
// Recursively traverse the entire seccompiler source folder and trigger a re-run of this build
117+
// script on any modification of these files.
118+
fn register_seccompiler_src_watchlist(src_dir: &Path) {
119+
let contents = fs::read_dir(src_dir).expect("Unable to read folder contents.");
120+
for entry in contents {
121+
let path = entry.unwrap().path();
122+
let metadata = fs::metadata(&path).expect("Unable to read file/folder metadata.");
123+
124+
if metadata.is_file() {
125+
// Watch all source files.
126+
println!(
127+
"cargo:rerun-if-changed={}",
128+
path.to_str().expect("Invalid unicode bytes.")
129+
);
130+
} else if metadata.is_dir() {
131+
// If is a folder, recurse.
132+
register_seccompiler_src_watchlist(&path);
133+
}
134+
}
135+
}
136+
137+
fn cpuid() {
138+
// Sets a `--cfg` flag for conditional compilation.
139+
//
140+
// TODO: Use `core::arch::x86_64::has_cpuid`
141+
// (https://github.com/firecracker-microvm/firecracker/issues/3271).
142+
#[cfg(any(
143+
all(target_arch = "x86", target_feature = "sse", not(target_env = "sgx")),
144+
all(target_arch = "x86_64", not(target_env = "sgx"))
145+
))]
146+
println!("cargo:rustc-cfg=cpuid");
12147
}

src/api_server/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl ApiServer {
101101
/// use utils::eventfd::EventFd;
102102
/// use utils::tempfile::TempFile;
103103
/// use vmm::rpc_interface::VmmData;
104-
/// use vmm::seccomp_filters::{get_filters, SeccompConfig};
104+
/// use vmm::seccomp_filters::get_empty_filters;
105105
/// use vmm::vmm_config::instance_info::InstanceInfo;
106106
/// use vmm::HTTP_MAX_PAYLOAD_SIZE;
107107
///
@@ -113,7 +113,7 @@ impl ApiServer {
113113
/// let (api_request_sender, _from_api) = channel();
114114
/// let (to_api, vmm_response_receiver) = channel();
115115
/// let time_reporter = ProcessTimeReporter::new(Some(1), Some(1), Some(1));
116-
/// let seccomp_filters = get_filters(SeccompConfig::None).unwrap();
116+
/// let seccomp_filters = get_empty_filters();
117117
/// let payload_limit = HTTP_MAX_PAYLOAD_SIZE;
118118
/// let (socket_ready_sender, socket_ready_receiver): (Sender<bool>, Receiver<bool>) = channel();
119119
///
@@ -315,7 +315,7 @@ mod tests {
315315
use utils::time::ClockType;
316316
use vmm::builder::StartMicrovmError;
317317
use vmm::rpc_interface::VmmActionError;
318-
use vmm::seccomp_filters::{get_filters, SeccompConfig};
318+
use vmm::seccomp_filters::get_empty_filters;
319319
use vmm::vmm_config::instance_info::InstanceInfo;
320320
use vmm::vmm_config::snapshot::CreateSnapshotParams;
321321

@@ -490,7 +490,7 @@ mod tests {
490490
let to_vmm_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
491491
let (api_request_sender, _from_api) = channel();
492492
let (to_api, vmm_response_receiver) = channel();
493-
let seccomp_filters = get_filters(SeccompConfig::Advanced).unwrap();
493+
let seccomp_filters = get_empty_filters();
494494
let (socket_ready_sender, socket_ready_receiver) = channel();
495495

496496
thread::Builder::new()
@@ -538,7 +538,7 @@ mod tests {
538538
let to_vmm_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
539539
let (api_request_sender, _from_api) = channel();
540540
let (_to_api, vmm_response_receiver) = channel();
541-
let seccomp_filters = get_filters(SeccompConfig::Advanced).unwrap();
541+
let seccomp_filters = get_empty_filters();
542542
let (socket_ready_sender, socket_ready_receiver) = channel();
543543

544544
thread::Builder::new()

src/cpu-template-helper/src/utils/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex};
1010

1111
use vmm::builder::{build_microvm_for_boot, StartMicrovmError};
1212
use vmm::resources::VmResources;
13-
use vmm::seccomp_filters::{get_filters, SeccompConfig};
13+
use vmm::seccomp_filters::get_empty_filters;
1414
use vmm::vmm_config::instance_info::{InstanceInfo, VmState};
1515
use vmm::{EventManager, Vmm, HTTP_MAX_PAYLOAD_SIZE};
1616

@@ -100,7 +100,7 @@ pub fn build_microvm_from_config(config: &str) -> Result<(Arc<Mutex<Vmm>>, VmRes
100100
let vm_resources = VmResources::from_json(config, &instance_info, HTTP_MAX_PAYLOAD_SIZE, None)
101101
.map_err(Error::CreateVmResources)?;
102102
let mut event_manager = EventManager::new().unwrap();
103-
let seccomp_filters = get_filters(SeccompConfig::None).unwrap();
103+
let seccomp_filters = get_empty_filters();
104104

105105
// Build a microVM.
106106
let vmm = build_microvm_for_boot(

src/firecracker/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
mod api_server_adapter;
55
mod metrics;
6+
mod seccomp;
67

78
use std::fs::{self, File};
89
use std::path::PathBuf;
@@ -17,14 +18,15 @@ use utils::arg_parser::{ArgParser, Argument};
1718
use utils::terminal::Terminal;
1819
use utils::validators::validate_instance_id;
1920
use vmm::resources::VmResources;
20-
use vmm::seccomp_filters::{get_filters, SeccompConfig};
2121
use vmm::signal_handler::register_signal_handlers;
2222
use vmm::version_map::{FC_VERSION_TO_SNAP_VERSION, VERSION_MAP};
2323
use vmm::vmm_config::instance_info::{InstanceInfo, VmState};
2424
use vmm::vmm_config::logger::{init_logger, LoggerConfig, LoggerLevel};
2525
use vmm::vmm_config::metrics::{init_metrics, MetricsConfig};
2626
use vmm::{EventManager, FcExitCode, HTTP_MAX_PAYLOAD_SIZE};
2727

28+
use crate::seccomp::SeccompConfig;
29+
2830
// The reason we place default API socket under /run is that API socket is a
2931
// runtime file.
3032
// see https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s15.html for more information.
@@ -317,7 +319,7 @@ fn main_exitable() -> FcExitCode {
317319
arguments.flag_present("no-seccomp"),
318320
arguments.single_value("seccomp-filter"),
319321
)
320-
.and_then(get_filters)
322+
.and_then(seccomp::get_filters)
321323
{
322324
Ok(filters) => filters,
323325
Err(err) => {

src/vmm/src/seccomp_filters/mod.rs renamed to src/firecracker/src/seccomp.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
use std::fmt;
44
use std::fs::File;
55
use std::io::{BufReader, Read};
6-
use std::sync::Arc;
76

8-
use seccompiler::{deserialize_binary, BpfThreadMap, DeserializationError, InstallationError};
7+
use seccompiler::{deserialize_binary, BpfThreadMap, DeserializationError};
8+
use vmm::seccomp_filters::get_empty_filters;
99

1010
const THREAD_CATEGORIES: [&str; 3] = ["vmm", "api", "vcpu"];
1111

@@ -18,16 +18,12 @@ const DESERIALIZATION_BYTES_LIMIT: Option<u64> = Some(100_000);
1818
/// Error retrieving seccomp filters.
1919
#[derive(fmt::Debug)]
2020
pub enum FilterError {
21-
/// Invalid SeccompConfig.
22-
SeccompConfig(String),
2321
/// Filter deserialitaion error.
2422
Deserialization(DeserializationError),
2523
/// Invalid thread categories.
2624
ThreadCategories(String),
2725
/// Missing Thread Category.
2826
MissingThreadCategory(String),
29-
/// Filter installation error.
30-
Install(InstallationError),
3127
/// File open error.
3228
FileOpen(std::io::Error),
3329
}
@@ -37,17 +33,13 @@ impl fmt::Display for FilterError {
3733
use self::FilterError::*;
3834

3935
match *self {
40-
SeccompConfig(ref message) => {
41-
write!(f, "Invalid seccomp argument configuration: {}", message)
42-
}
4336
Deserialization(ref err) => write!(f, "Filter deserialization failed: {}", err),
4437
ThreadCategories(ref categories) => {
4538
write!(f, "Invalid thread categories: {}", categories)
4639
}
4740
MissingThreadCategory(ref category) => {
4841
write!(f, "Missing thread category: {}", category)
4942
}
50-
Install(ref err) => write!(f, "Filter installation error: {}", err),
5143
FileOpen(ref err) => write!(f, "Filter file open error: {}", err),
5244
}
5345
}
@@ -101,15 +93,6 @@ fn get_default_filters() -> Result<BpfThreadMap, FilterError> {
10193
filter_thread_categories(map)
10294
}
10395

104-
/// Retrieve empty seccomp filters.
105-
fn get_empty_filters() -> BpfThreadMap {
106-
let mut map = BpfThreadMap::new();
107-
map.insert("vmm".to_string(), Arc::new(vec![]));
108-
map.insert("api".to_string(), Arc::new(vec![]));
109-
map.insert("vcpu".to_string(), Arc::new(vec![]));
110-
map
111-
}
112-
11396
/// Retrieve custom seccomp filters.
11497
fn get_custom_filters<R: Read>(reader: R) -> Result<BpfThreadMap, FilterError> {
11598
let map = deserialize_binary(BufReader::new(reader), DESERIALIZATION_BYTES_LIMIT)
@@ -148,6 +131,8 @@ fn filter_thread_categories(map: BpfThreadMap) -> Result<BpfThreadMap, FilterErr
148131

149132
#[cfg(test)]
150133
mod tests {
134+
use std::sync::Arc;
135+
151136
use seccompiler::BpfThreadMap;
152137
use utils::tempfile::TempFile;
153138

0 commit comments

Comments
 (0)