@@ -5,7 +5,20 @@ use std::collections::HashMap;
5
5
use std:: io:: Read ;
6
6
use std:: sync:: Arc ;
7
7
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 ( ) ;
9
22
10
23
/// Each BPF instruction is 8 bytes long and 4 byte aligned.
11
24
/// 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];
22
35
pub type BpfThreadMap = HashMap < String , Arc < BpfProgram > > ;
23
36
24
37
/// Binary filter deserialization errors.
25
- pub type DeserializationError = bincode:: Error ;
38
+ pub type DeserializationError = bincode:: error :: DecodeError ;
26
39
27
40
/// Retrieve empty seccomp filters.
28
41
pub fn get_empty_filters ( ) -> BpfThreadMap {
@@ -34,19 +47,8 @@ pub fn get_empty_filters() -> BpfThreadMap {
34
47
}
35
48
36
49
/// 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 ) ?;
50
52
51
53
Ok ( result
52
54
. into_iter ( )
@@ -134,49 +136,28 @@ mod tests {
134
136
#[ test]
135
137
fn test_deserialize_binary ( ) {
136
138
// 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 ( ) ;
141
141
142
142
// Test that the binary deserialization is correct, and that the thread keys
143
143
// 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
+ ) ) ;
180
161
}
181
162
182
163
#[ test]
0 commit comments