Skip to content

Commit 6fa2064

Browse files
committed
chore: update bincode to 2.0
Follow the official migration guide for upgrading with serde support, described in [1] [1]: https://github.com/bincode-org/bincode/blob/trunk/docs/migration_guide.md#migrating-with-serde Signed-off-by: Patrick Roy <[email protected]>
1 parent 05e7881 commit 6fa2064

File tree

10 files changed

+113
-134
lines changed

10 files changed

+113
-134
lines changed

Cargo.lock

Lines changed: 25 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/firecracker/examples/seccomp/jailer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn main() {
1313
let bpf_path = &args[2];
1414

1515
let filter_file = File::open(bpf_path).unwrap();
16-
let map = deserialize_binary(&filter_file, None).unwrap();
16+
let map = deserialize_binary(&filter_file).unwrap();
1717

1818
// Loads filters.
1919
apply_filter(map.get("main").unwrap()).unwrap();

src/firecracker/examples/seccomp/panic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fn main() {
1111
let filter_thread = &args[2];
1212

1313
let filter_file = File::open(bpf_path).unwrap();
14-
let map = deserialize_binary(&filter_file, None).unwrap();
14+
let map = deserialize_binary(&filter_file).unwrap();
1515
apply_filter(map.get(filter_thread).unwrap()).unwrap();
1616
panic!("Expected panic.");
1717
}

src/firecracker/src/seccomp.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,6 @@ use vmm::seccomp::{BpfThreadMap, DeserializationError, deserialize_binary, get_e
99

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

12-
// This byte limit is passed to `bincode` to guard against a potential memory
13-
// allocation DOS caused by binary filters that are too large.
14-
// This limit can be safely determined since the maximum length of a BPF
15-
// filter is 4096 instructions and Firecracker has a finite number of threads.
16-
const DESERIALIZATION_BYTES_LIMIT: Option<u64> = Some(100_000);
17-
1812
/// Error retrieving seccomp filters.
1913
#[derive(Debug, thiserror::Error, displaydoc::Display)]
2014
pub enum FilterError {
@@ -72,15 +66,13 @@ pub fn get_filters(config: SeccompConfig) -> Result<BpfThreadMap, FilterError> {
7266
fn get_default_filters() -> Result<BpfThreadMap, FilterError> {
7367
// Retrieve, at compile-time, the serialized binary filter generated with seccompiler.
7468
let bytes: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/seccomp_filter.bpf"));
75-
let map = deserialize_binary(bytes, DESERIALIZATION_BYTES_LIMIT)
76-
.map_err(FilterError::Deserialization)?;
69+
let map = deserialize_binary(bytes).map_err(FilterError::Deserialization)?;
7770
filter_thread_categories(map)
7871
}
7972

8073
/// Retrieve custom seccomp filters.
8174
fn get_custom_filters<R: Read + Debug>(reader: R) -> Result<BpfThreadMap, FilterError> {
82-
let map = deserialize_binary(BufReader::new(reader), DESERIALIZATION_BYTES_LIMIT)
83-
.map_err(FilterError::Deserialization)?;
75+
let map = deserialize_binary(BufReader::new(reader)).map_err(FilterError::Deserialization)?;
8476
filter_thread_categories(map)
8577
}
8678

src/seccompiler/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ path = "src/bin.rs"
1616
bench = false
1717

1818
[dependencies]
19-
bincode = "1.2.1"
19+
bincode = { version = "2.0.1", features = ["serde"] }
2020
clap = { version = "4.5.32", features = ["derive", "string"] }
2121
displaydoc = "0.2.5"
2222
libc = "0.2.171"

src/seccompiler/src/lib.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use std::os::fd::{AsRawFd, FromRawFd};
88
use std::os::unix::fs::MetadataExt;
99
use std::str::FromStr;
1010

11-
use bincode::Error as BincodeError;
11+
use bincode::config;
12+
use bincode::config::{Configuration, Fixint, Limit, LittleEndian};
13+
use bincode::error::EncodeError as BincodeError;
1214

1315
mod bindings;
1416
use bindings::*;
@@ -17,6 +19,18 @@ pub mod types;
1719
pub use types::*;
1820
use zerocopy::IntoBytes;
1921

22+
// This byte limit is passed to `bincode` to guard against a potential memory
23+
// allocation DOS caused by binary filters that are too large.
24+
// This limit can be safely determined since the maximum length of a BPF
25+
// filter is 4096 instructions and Firecracker has a finite number of threads.
26+
const DESERIALIZATION_BYTES_LIMIT: usize = 100_000;
27+
28+
pub const BINCODE_CONFIG: Configuration<LittleEndian, Fixint, Limit<DESERIALIZATION_BYTES_LIMIT>> =
29+
config::standard()
30+
.with_fixed_int_encoding()
31+
.with_limit::<DESERIALIZATION_BYTES_LIMIT>()
32+
.with_little_endian();
33+
2034
/// Binary filter compilation errors.
2135
#[derive(Debug, thiserror::Error, displaydoc::Display)]
2236
pub enum CompilationError {
@@ -174,8 +188,9 @@ pub fn compile_bpf(
174188
bpf_map.insert(name.clone(), bpf);
175189
}
176190

177-
let output_file = File::create(out_path).map_err(CompilationError::OutputCreate)?;
191+
let mut output_file = File::create(out_path).map_err(CompilationError::OutputCreate)?;
178192

179-
bincode::serialize_into(output_file, &bpf_map).map_err(CompilationError::BincodeSerialize)?;
193+
bincode::encode_into_std_write(&bpf_map, &mut output_file, BINCODE_CONFIG)
194+
.map_err(CompilationError::BincodeSerialize)?;
180195
Ok(())
181196
}

src/vmm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ aes-gcm = { version = "0.10.1", default-features = false, features = ["aes"] }
1414
arrayvec = { version = "0.7.6", optional = true }
1515
aws-lc-rs = { version = "1.12.6", features = ["bindgen"] }
1616
base64 = "0.22.1"
17-
bincode = "1.2.1"
17+
bincode = { version = "2.0.1", features = ["serde"] }
1818
bitflags = "2.9.0"
1919
crc64 = "2.0.0"
2020
derive_more = { version = "2.0.1", default-features = false, features = ["from", "display"] }

src/vmm/src/mmds/token.rs

Lines changed: 13 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use std::{fmt, io};
1010

1111
use aes_gcm::{AeadInPlace, Aes256Gcm, Key, KeyInit, Nonce};
1212
use base64::Engine;
13-
use bincode::{DefaultOptions, Error as BincodeError, Options};
13+
use bincode::config;
14+
use bincode::config::{Configuration, Fixint, Limit, LittleEndian};
1415
use serde::{Deserialize, Serialize};
1516
use utils::time::{ClockType, get_time_ms};
1617

@@ -45,6 +46,12 @@ const TOKEN_LENGTH_LIMIT: usize = 70;
4546
/// too much memory when deserializing tokens.
4647
const DESERIALIZATION_BYTES_LIMIT: usize = std::mem::size_of::<Token>();
4748

49+
const BINCODE_CONFIG: Configuration<LittleEndian, Fixint, Limit<DESERIALIZATION_BYTES_LIMIT>> =
50+
config::standard()
51+
.with_fixed_int_encoding()
52+
.with_limit::<DESERIALIZATION_BYTES_LIMIT>()
53+
.with_little_endian();
54+
4855
#[rustfmt::skip]
4956
#[derive(Debug, thiserror::Error, displaydoc::Display)]
5057
pub enum MmdsTokenError {
@@ -57,7 +64,7 @@ pub enum MmdsTokenError {
5764
/// Invalid time to live value provided for token: {0}. Please provide a value between {MIN_TOKEN_TTL_SECONDS:} and {MAX_TOKEN_TTL_SECONDS:}.
5865
InvalidTtlValue(u32),
5966
/// Bincode serialization failed: {0}.
60-
Serialization(#[from] BincodeError),
67+
Serialization(#[from] bincode::error::EncodeError),
6168
/// Failed to encrypt token.
6269
TokenEncryption,
6370
}
@@ -287,7 +294,7 @@ impl Token {
287294

288295
/// Encode token structure into a string using base64 encoding.
289296
fn base64_encode(&self) -> Result<String, MmdsTokenError> {
290-
let token_bytes: Vec<u8> = bincode::serialize(self)?;
297+
let token_bytes: Vec<u8> = bincode::serde::encode_to_vec(self, BINCODE_CONFIG)?;
291298

292299
// Encode token structure bytes into base64.
293300
Ok(base64::engine::general_purpose::STANDARD.encode(token_bytes))
@@ -299,12 +306,9 @@ impl Token {
299306
.decode(encoded_token)
300307
.map_err(|_| MmdsTokenError::ExpiryExtraction)?;
301308

302-
let token: Token = DefaultOptions::new()
303-
.with_fixint_encoding()
304-
.allow_trailing_bytes()
305-
.with_limit(DESERIALIZATION_BYTES_LIMIT as u64)
306-
.deserialize(&token_bytes)
307-
.map_err(|_| MmdsTokenError::ExpiryExtraction)?;
309+
let token: Token = bincode::serde::decode_from_slice(&token_bytes, BINCODE_CONFIG)
310+
.map_err(|_| MmdsTokenError::ExpiryExtraction)?
311+
.0;
308312
Ok(token)
309313
}
310314
}
@@ -518,45 +522,4 @@ mod tests {
518522
assert!(!token_authority.is_valid(&token0));
519523
assert!(!token_authority.is_valid(&token1));
520524
}
521-
522-
#[test]
523-
fn test_error_display() {
524-
assert_eq!(
525-
MmdsTokenError::EntropyPool(io::Error::from_raw_os_error(0)).to_string(),
526-
format!(
527-
"Failed to extract entropy from /dev/urandom entropy pool: {}.",
528-
io::Error::from_raw_os_error(0)
529-
)
530-
);
531-
532-
assert_eq!(
533-
MmdsTokenError::ExpiryExtraction.to_string(),
534-
"Failed to extract expiry value from token."
535-
);
536-
537-
assert_eq!(
538-
MmdsTokenError::InvalidState.to_string(),
539-
"Invalid token authority state."
540-
);
541-
542-
assert_eq!(
543-
MmdsTokenError::InvalidTtlValue(0).to_string(),
544-
format!(
545-
"Invalid time to live value provided for token: 0. Please provide a value between \
546-
{} and {}.",
547-
MIN_TOKEN_TTL_SECONDS, MAX_TOKEN_TTL_SECONDS
548-
)
549-
);
550-
551-
assert_eq!(
552-
MmdsTokenError::Serialization(BincodeError::new(bincode::ErrorKind::SizeLimit))
553-
.to_string(),
554-
"Bincode serialization failed: the size limit has been reached."
555-
);
556-
557-
assert_eq!(
558-
MmdsTokenError::TokenEncryption.to_string(),
559-
"Failed to encrypt token."
560-
);
561-
}
562525
}

src/vmm/src/seccomp.rs

Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@ use std::collections::HashMap;
55
use std::io::Read;
66
use std::sync::Arc;
77

8-
use bincode::{DefaultOptions, Options};
8+
use bincode::config;
9+
use bincode::config::{Configuration, Fixint, Limit, LittleEndian};
10+
11+
// This byte limit is passed to `bincode` to guard against a potential memory
12+
// allocation DOS caused by binary filters that are too large.
13+
// This limit can be safely determined since the maximum length of a BPF
14+
// filter is 4096 instructions and Firecracker has a finite number of threads.
15+
const DESERIALIZATION_BYTES_LIMIT: usize = 100_000;
16+
17+
const BINCODE_CONFIG: Configuration<LittleEndian, Fixint, Limit<DESERIALIZATION_BYTES_LIMIT>> =
18+
config::standard()
19+
.with_fixed_int_encoding()
20+
.with_limit::<DESERIALIZATION_BYTES_LIMIT>()
21+
.with_little_endian();
922

1023
/// Each BPF instruction is 8 bytes long and 4 byte aligned.
1124
/// This alignment needs to be satisfied in order for a BPF code to be accepted
@@ -22,7 +35,7 @@ pub type BpfProgramRef<'a> = &'a [BpfInstruction];
2235
pub type BpfThreadMap = HashMap<String, Arc<BpfProgram>>;
2336

2437
/// Binary filter deserialization errors.
25-
pub type DeserializationError = bincode::Error;
38+
pub type DeserializationError = bincode::error::DecodeError;
2639

2740
/// Retrieve empty seccomp filters.
2841
pub fn get_empty_filters() -> BpfThreadMap {
@@ -34,19 +47,8 @@ pub fn get_empty_filters() -> BpfThreadMap {
3447
}
3548

3649
/// Deserialize binary with bpf filters
37-
pub fn deserialize_binary<R: Read>(
38-
reader: R,
39-
bytes_limit: Option<u64>,
40-
) -> Result<BpfThreadMap, DeserializationError> {
41-
let result = match bytes_limit {
42-
Some(limit) => DefaultOptions::new()
43-
.with_fixint_encoding()
44-
.allow_trailing_bytes()
45-
.with_limit(limit)
46-
.deserialize_from::<R, HashMap<String, BpfProgram>>(reader),
47-
// No limit is the default.
48-
None => bincode::deserialize_from::<R, HashMap<String, BpfProgram>>(reader),
49-
}?;
50+
pub fn deserialize_binary<R: Read>(mut reader: R) -> Result<BpfThreadMap, DeserializationError> {
51+
let result: HashMap<String, _> = bincode::decode_from_std_read(&mut reader, BINCODE_CONFIG)?;
5052

5153
Ok(result
5254
.into_iter()
@@ -134,49 +136,28 @@ mod tests {
134136
#[test]
135137
fn test_deserialize_binary() {
136138
// Malformed bincode binary.
137-
{
138-
let data = "adassafvc".to_string();
139-
deserialize_binary(data.as_bytes(), None).unwrap_err();
140-
}
139+
let data = "adassafvc".to_string();
140+
deserialize_binary(data.as_bytes()).unwrap_err();
141141

142142
// Test that the binary deserialization is correct, and that the thread keys
143143
// have been lowercased.
144-
{
145-
let bpf_prog = vec![0; 2];
146-
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
147-
filter_map.insert("VcpU".to_string(), bpf_prog.clone());
148-
let bytes = bincode::serialize(&filter_map).unwrap();
149-
150-
let mut expected_res = BpfThreadMap::new();
151-
expected_res.insert("vcpu".to_string(), Arc::new(bpf_prog));
152-
assert_eq!(deserialize_binary(&bytes[..], None).unwrap(), expected_res);
153-
}
154-
155-
// Test deserialization with binary_limit.
156-
{
157-
let bpf_prog = vec![0; 2];
158-
159-
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
160-
filter_map.insert("t1".to_string(), bpf_prog.clone());
161-
162-
let bytes = bincode::serialize(&filter_map).unwrap();
163-
164-
// Binary limit too low.
165-
assert!(matches!(
166-
deserialize_binary(&bytes[..], Some(20)).unwrap_err(),
167-
error
168-
if error.to_string() == "the size limit has been reached"
169-
));
170-
171-
let mut expected_res = BpfThreadMap::new();
172-
expected_res.insert("t1".to_string(), Arc::new(bpf_prog));
173-
174-
// Correct binary limit.
175-
assert_eq!(
176-
deserialize_binary(&bytes[..], Some(50)).unwrap(),
177-
expected_res
178-
);
179-
}
144+
let bpf_prog = vec![0; 2];
145+
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
146+
filter_map.insert("VcpU".to_string(), bpf_prog.clone());
147+
let bytes = bincode::serde::encode_to_vec(&filter_map, BINCODE_CONFIG).unwrap();
148+
149+
let mut expected_res = BpfThreadMap::new();
150+
expected_res.insert("vcpu".to_string(), Arc::new(bpf_prog));
151+
assert_eq!(deserialize_binary(&bytes[..]).unwrap(), expected_res);
152+
153+
let bpf_prog = vec![0; DESERIALIZATION_BYTES_LIMIT + 1];
154+
let mut filter_map: HashMap<String, BpfProgram> = HashMap::new();
155+
filter_map.insert("VcpU".to_string(), bpf_prog.clone());
156+
let bytes = bincode::serde::encode_to_vec(&filter_map, BINCODE_CONFIG).unwrap();
157+
assert!(matches!(
158+
deserialize_binary(&bytes[..]).unwrap_err(),
159+
bincode::error::DecodeError::LimitExceeded
160+
));
180161
}
181162

182163
#[test]

0 commit comments

Comments
 (0)