Skip to content

Commit d3725d5

Browse files
committed
test: move seccomp things into vmm
Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 35f6ec0 commit d3725d5

File tree

17 files changed

+252
-255
lines changed

17 files changed

+252
-255
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/firecracker/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ libc = "0.2.164"
2222
log-instrument = { path = "../log-instrument", optional = true }
2323
micro_http = { git = "https://github.com/firecracker-microvm/micro-http" }
2424

25-
seccompiler = { path = "../seccompiler" }
2625
serde = { version = "1.0.215", features = ["derive"] }
2726
serde_derive = "1.0.136"
2827
serde_json = "1.0.133"

src/firecracker/src/api_server/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::sync::mpsc;
1414

1515
pub use micro_http::{Body, HttpServer, Request, Response, ServerError, StatusCode, Version};
1616
use parsed_request::{ParsedRequest, RequestAction};
17-
use seccompiler::BpfProgramRef;
17+
use vmm::seccomp::BpfProgramRef;
1818
use serde_json::json;
1919
use utils::time::{get_time_us, ClockType};
2020
use vmm::logger::{
@@ -78,7 +78,7 @@ impl ApiServer {
7878
// Load seccomp filters on the API thread.
7979
// Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters
8080
// altogether is the desired behaviour.
81-
if let Err(err) = seccompiler::apply_filter(seccomp_filter) {
81+
if let Err(err) = vmm::seccomp::apply_filter(seccomp_filter) {
8282
panic!(
8383
"Failed to set the requested seccomp filters on the API thread: {}",
8484
err

src/firecracker/src/api_server_adapter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex};
88
use std::thread;
99

1010
use event_manager::{EventOps, Events, MutEventSubscriber, SubscriberOps};
11-
use seccompiler::BpfThreadMap;
11+
use vmm::seccomp::BpfThreadMap;
1212
use vmm::logger::{error, warn, ProcessTimeReporter};
1313
use vmm::resources::VmResources;
1414
use vmm::rpc_interface::{

src/firecracker/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::{io, panic};
1717
use api_server_adapter::ApiServerError;
1818
use event_manager::SubscriberOps;
1919
use seccomp::FilterError;
20-
use seccompiler::BpfThreadMap;
20+
use vmm::seccomp::BpfThreadMap;
2121
use utils::arg_parser::{ArgParser, Argument};
2222
use utils::validators::validate_instance_id;
2323
use vmm::builder::StartMicrovmError;

src/firecracker/src/seccomp.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::fs::File;
55
use std::io::{BufReader, Read};
66
use std::path::Path;
77

8-
use seccompiler::{deserialize_binary, BpfThreadMap, DeserializationError};
9-
use vmm::seccomp_filters::get_empty_filters;
8+
use vmm::seccomp::get_empty_filters;
9+
use vmm::seccomp::{deserialize_binary, BpfThreadMap, DeserializationError};
1010

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

@@ -118,7 +118,7 @@ fn filter_thread_categories(map: BpfThreadMap) -> Result<BpfThreadMap, FilterErr
118118
mod tests {
119119
use std::sync::Arc;
120120

121-
use seccompiler::BpfThreadMap;
121+
use vmm::seccomp::BpfThreadMap;
122122
use vmm_sys_util::tempfile::TempFile;
123123

124124
use super::*;

src/seccompiler/src/lib.rs

Lines changed: 2 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

3-
use std::collections::{BTreeMap, HashMap};
3+
use std::collections::BTreeMap;
44
use std::fs::File;
55
use std::io::Read;
66
use std::os::fd::FromRawFd;
7-
use std::sync::Arc;
87

9-
use bincode::{DefaultOptions, Error as BincodeError, Options};
8+
use bincode::Error as BincodeError;
109
use libseccomp::*;
1110

1211
pub mod types;
@@ -125,208 +124,3 @@ pub fn compile_bpf(
125124
bincode::serialize_into(output_file, &bpf_map).map_err(CompilationError::BincodeSerialize)?;
126125
Ok(())
127126
}
128-
129-
/// Binary filter deserialization errors.
130-
#[derive(Debug, thiserror::Error, displaydoc::Display)]
131-
pub enum DeserializationError {
132-
/// Bincode deserialization failed: {0}
133-
Bincode(BincodeError),
134-
}
135-
136-
pub fn deserialize_binary<R: Read>(
137-
reader: R,
138-
bytes_limit: Option<u64>,
139-
) -> Result<BpfThreadMap, DeserializationError> {
140-
let result = match bytes_limit {
141-
Some(limit) => DefaultOptions::new()
142-
.with_fixint_encoding()
143-
.allow_trailing_bytes()
144-
.with_limit(limit)
145-
.deserialize_from::<R, HashMap<String, BpfProgram>>(reader),
146-
// No limit is the default.
147-
None => bincode::deserialize_from::<R, HashMap<String, BpfProgram>>(reader),
148-
}
149-
.map_err(DeserializationError::Bincode)?;
150-
151-
Ok(result
152-
.into_iter()
153-
.map(|(k, v)| (k.to_lowercase(), Arc::new(v)))
154-
.collect())
155-
}
156-
157-
/// Filter installation errors.
158-
#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
159-
pub enum InstallationError {
160-
/// Filter length exceeds the maximum size of {BPF_MAX_LEN:} instructions
161-
FilterTooLarge,
162-
/// prctl` syscall failed with error code: {0}
163-
Prctl(i32),
164-
}
165-
166-
/// The maximum seccomp-BPF program length allowed by the linux kernel.
167-
pub const BPF_MAX_LEN: usize = 4096;
168-
169-
// BPF structure definition for filter array.
170-
// See /usr/include/linux/filter.h .
171-
#[repr(C)]
172-
#[derive(Debug)]
173-
pub struct SockFprog {
174-
pub len: u16,
175-
pub filter: *const u8,
176-
}
177-
178-
pub fn apply_filter(bpf_filter: BpfProgramRef) -> Result<(), InstallationError> {
179-
// If the program is empty, don't install the filter.
180-
if bpf_filter.is_empty() {
181-
return Ok(());
182-
}
183-
184-
// If the program length is greater than the limit allowed by the kernel,
185-
// fail quickly. Otherwise, `prctl` will give a more cryptic error code.
186-
if BPF_MAX_LEN < bpf_filter.len() {
187-
return Err(InstallationError::FilterTooLarge);
188-
}
189-
190-
let bpf_filter_len =
191-
u16::try_from(bpf_filter.len()).map_err(|_| InstallationError::FilterTooLarge)?;
192-
193-
// SAFETY: Safe because the parameters are valid.
194-
unsafe {
195-
{
196-
let rc = libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
197-
if rc != 0 {
198-
return Err(InstallationError::Prctl(*libc::__errno_location()));
199-
}
200-
}
201-
202-
let bpf_prog = SockFprog {
203-
len: bpf_filter_len,
204-
filter: bpf_filter.as_ptr(),
205-
};
206-
let bpf_prog_ptr = &bpf_prog as *const SockFprog;
207-
{
208-
let rc = libc::prctl(
209-
libc::PR_SET_SECCOMP,
210-
libc::SECCOMP_MODE_FILTER,
211-
bpf_prog_ptr,
212-
);
213-
if rc != 0 {
214-
return Err(InstallationError::Prctl(*libc::__errno_location()));
215-
}
216-
}
217-
}
218-
219-
Ok(())
220-
}
221-
222-
#[cfg(test)]
223-
mod tests {
224-
#![allow(clippy::undocumented_unsafe_blocks)]
225-
226-
use std::collections::HashMap;
227-
use std::sync::Arc;
228-
use std::thread;
229-
230-
use super::*;
231-
232-
#[test]
233-
fn test_deserialize_binary() {
234-
// Malformed bincode binary.
235-
{
236-
let data = "adassafvc".to_string();
237-
deserialize_binary(data.as_bytes(), None).unwrap_err();
238-
}
239-
240-
// Test that the binary deserialization is correct, and that the thread keys
241-
// have been lowercased.
242-
{
243-
let bpf_prog = vec![0; 32];
244-
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
245-
filter_map.insert("VcpU".to_string(), bpf_prog.clone());
246-
let bytes = bincode::serialize(&filter_map).unwrap();
247-
248-
let mut expected_res = BpfThreadMap::new();
249-
expected_res.insert("vcpu".to_string(), Arc::new(bpf_prog));
250-
assert_eq!(deserialize_binary(&bytes[..], None).unwrap(), expected_res);
251-
}
252-
253-
// Test deserialization with binary_limit.
254-
{
255-
let bpf_prog = vec![0; 32];
256-
257-
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
258-
filter_map.insert("t1".to_string(), bpf_prog.clone());
259-
260-
let bytes = bincode::serialize(&filter_map).unwrap();
261-
262-
// Binary limit too low.
263-
assert!(matches!(
264-
deserialize_binary(&bytes[..], Some(16)).unwrap_err(),
265-
DeserializationError::Bincode(error)
266-
if error.to_string() == "the size limit has been reached"
267-
));
268-
269-
let mut expected_res = BpfThreadMap::new();
270-
expected_res.insert("t1".to_string(), Arc::new(bpf_prog));
271-
272-
// Correct binary limit.
273-
assert_eq!(
274-
deserialize_binary(&bytes[..], Some(64)).unwrap(),
275-
expected_res
276-
);
277-
}
278-
}
279-
280-
#[test]
281-
fn test_filter_apply() {
282-
// Test filter too large.
283-
thread::spawn(|| {
284-
let filter: BpfProgram = vec![0; 4096 * 2];
285-
286-
// Apply seccomp filter.
287-
assert_eq!(
288-
apply_filter(&filter).unwrap_err(),
289-
InstallationError::FilterTooLarge
290-
);
291-
})
292-
.join()
293-
.unwrap();
294-
295-
// Test empty filter.
296-
thread::spawn(|| {
297-
let filter: BpfProgram = vec![];
298-
299-
assert_eq!(filter.len(), 0);
300-
301-
let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) };
302-
assert_eq!(seccomp_level, 0);
303-
304-
apply_filter(&filter).unwrap();
305-
306-
// test that seccomp level remains 0 on failure.
307-
let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) };
308-
assert_eq!(seccomp_level, 0);
309-
})
310-
.join()
311-
.unwrap();
312-
313-
// Test invalid BPF code.
314-
thread::spawn(|| {
315-
let filter = vec![0xFF; 32];
316-
317-
let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) };
318-
assert_eq!(seccomp_level, 0);
319-
320-
assert_eq!(
321-
apply_filter(&filter).unwrap_err(),
322-
InstallationError::Prctl(22)
323-
);
324-
325-
// test that seccomp level remains 0 on failure.
326-
let seccomp_level = unsafe { libc::prctl(libc::PR_GET_SECCOMP) };
327-
assert_eq!(seccomp_level, 0);
328-
})
329-
.join()
330-
.unwrap();
331-
}
332-
}

src/seccompiler/src/types.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22

3-
use std::collections::{BTreeMap, HashMap};
4-
use std::sync::Arc;
3+
use std::collections::BTreeMap;
54

65
use libseccomp::{ScmpAction, ScmpArch, ScmpCompareOp};
76
use serde::*;
@@ -91,15 +90,6 @@ pub struct Filter {
9190
#[derive(Debug, Deserialize)]
9291
pub struct BpfJson(pub BTreeMap<String, Filter>);
9392

94-
/// Program made up of a sequence of BPF instructions.
95-
pub type BpfProgram = Vec<u8>;
96-
97-
/// Reference to program made up of a sequence of BPF instructions.
98-
pub type BpfProgramRef<'a> = &'a [u8];
99-
100-
/// Type that associates a thread category to a BPF program.
101-
pub type BpfThreadMap = HashMap<String, Arc<BpfProgram>>;
102-
10393
/// Supported target architectures.
10494
#[derive(Debug)]
10595
pub enum TargetArch {

src/vmm/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ log-instrument = { path = "../log-instrument", optional = true }
3232
memfd = "0.6.3"
3333
micro_http = { git = "https://github.com/firecracker-microvm/micro-http" }
3434

35-
seccompiler = { path = "../seccompiler" }
3635
semver = { version = "1.0.23", features = ["serde"] }
3736
serde = { version = "1.0.215", features = ["derive", "rc"] }
3837
serde_json = "1.0.133"

src/vmm/src/builder.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use linux_loader::loader::elf::Elf as Loader;
1919
#[cfg(target_arch = "aarch64")]
2020
use linux_loader::loader::pe::PE as Loader;
2121
use linux_loader::loader::KernelLoader;
22-
use seccompiler::BpfThreadMap;
22+
use crate::seccomp::BpfThreadMap;
2323
use userfaultfd::Uffd;
2424
use utils::time::TimestampUs;
2525
use vm_memory::ReadVolatile;
@@ -372,7 +372,7 @@ pub fn build_microvm_for_boot(
372372
// Execution panics if filters cannot be loaded, use --no-seccomp if skipping filters
373373
// altogether is the desired behaviour.
374374
// Keep this as the last step before resuming vcpus.
375-
seccompiler::apply_filter(
375+
crate::seccomp::apply_filter(
376376
seccomp_filters
377377
.get("vmm")
378378
.ok_or_else(|| MissingSeccompFilters("vmm".to_string()))?,
@@ -443,7 +443,7 @@ pub enum BuildMicrovmFromSnapshotError {
443443
/// Failed to apply VMM secccomp filter as none found.
444444
MissingVmmSeccompFilters,
445445
/// Failed to apply VMM secccomp filter: {0}
446-
SeccompFiltersInternal(#[from] seccompiler::InstallationError),
446+
SeccompFiltersInternal(#[from] crate::seccomp::InstallationError),
447447
/// Failed to restore ACPI device manager: {0}
448448
ACPIDeviManager(#[from] ACPIDeviceManagerRestoreError),
449449
/// VMGenID update failed: {0}
@@ -559,7 +559,7 @@ pub fn build_microvm_from_snapshot(
559559

560560
// Load seccomp filters for the VMM thread.
561561
// Keep this as the last step of the building process.
562-
seccompiler::apply_filter(
562+
crate::seccomp::apply_filter(
563563
seccomp_filters
564564
.get("vmm")
565565
.ok_or(BuildMicrovmFromSnapshotError::MissingVmmSeccompFilters)?,

0 commit comments

Comments
 (0)