|
1 | 1 | // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
2 | 2 |
|
3 | | -use std::collections::{BTreeMap, HashMap}; |
| 3 | +use std::collections::BTreeMap; |
4 | 4 | use std::fs::File; |
5 | 5 | use std::io::Read; |
6 | 6 | use std::os::fd::FromRawFd; |
7 | | -use std::sync::Arc; |
8 | 7 |
|
9 | | -use bincode::{DefaultOptions, Error as BincodeError, Options}; |
| 8 | +use bincode::Error as BincodeError; |
10 | 9 | use libseccomp::*; |
11 | 10 |
|
12 | 11 | pub mod types; |
@@ -125,208 +124,3 @@ pub fn compile_bpf( |
125 | 124 | bincode::serialize_into(output_file, &bpf_map).map_err(CompilationError::BincodeSerialize)?; |
126 | 125 | Ok(()) |
127 | 126 | } |
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 | | -} |
0 commit comments