Skip to content

Commit ba6d19e

Browse files
committed
seccomp: forbid duplicate thread names
Signed-off-by: alindima <[email protected]>
1 parent 3fa08de commit ba6d19e

File tree

2 files changed

+64
-5
lines changed

2 files changed

+64
-5
lines changed

src/seccompiler/src/compiler.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@
2020
use std::collections::HashMap;
2121
use std::convert::{Into, TryInto};
2222
use std::fmt;
23+
use std::result;
2324

2425
use crate::backend::{
2526
Comment, Error as SeccompFilterError, SeccompAction, SeccompCondition, SeccompFilter,
2627
SeccompRule, SeccompRuleMap, TargetArch,
2728
};
2829
use crate::common::BpfProgram;
2930
use crate::syscall_table::SyscallTable;
31+
use serde::de::{self, Error as _, MapAccess, Visitor};
3032
use serde::Deserialize;
3133

32-
type Result<T> = std::result::Result<T, Error>;
34+
type Result<T> = result::Result<T, Error>;
3335

3436
/// Errors compiling Filters into BPF.
3537
#[derive(Debug, PartialEq)]
@@ -58,6 +60,43 @@ impl fmt::Display for Error {
5860
}
5961
}
6062

63+
/// Deserializable object that represents the Json filter file.
64+
pub(crate) struct JsonFile(pub HashMap<String, Filter>);
65+
66+
// Implement a custom deserializer, that returns an error for duplicate thread keys.
67+
impl<'de> Deserialize<'de> for JsonFile {
68+
fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
69+
where
70+
D: de::Deserializer<'de>,
71+
{
72+
struct JsonFileVisitor;
73+
74+
impl<'d> Visitor<'d> for JsonFileVisitor {
75+
type Value = HashMap<String, Filter>;
76+
77+
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
78+
f.write_str("a map of filters")
79+
}
80+
81+
fn visit_map<M>(self, mut access: M) -> result::Result<Self::Value, M::Error>
82+
where
83+
M: MapAccess<'d>,
84+
{
85+
let mut values = Self::Value::with_capacity(access.size_hint().unwrap_or(0));
86+
87+
while let Some((key, value)) = access.next_entry()? {
88+
if values.insert(key, value).is_some() {
89+
return Err(M::Error::custom("duplicate filter key"));
90+
};
91+
}
92+
93+
Ok(values)
94+
}
95+
}
96+
Ok(JsonFile(deserializer.deserialize_map(JsonFileVisitor)?))
97+
}
98+
}
99+
61100
/// Deserializable object representing a syscall rule.
62101
#[derive(Debug, Deserialize, PartialEq, Clone)]
63102
#[serde(deny_unknown_fields)]

src/seccompiler/src/seccompiler_bin.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use std::{fmt, io, process};
4747
use backend::{TargetArch, TargetArchError};
4848
use bincode::Error as BincodeError;
4949
use common::BpfProgram;
50-
use compiler::{Compiler, Error as FilterFormatError, Filter};
50+
use compiler::{Compiler, Error as FilterFormatError, JsonFile};
5151
use serde_json::error::Error as JSONError;
5252
use utils::arg_parser::{ArgParser, Argument, Arguments as ArgumentsBag};
5353

@@ -158,7 +158,7 @@ fn get_argument_values(arguments: &ArgumentsBag) -> Result<Arguments> {
158158
})
159159
}
160160

161-
fn parse_json(reader: &mut dyn Read) -> Result<HashMap<String, Filter>> {
161+
fn parse_json(reader: &mut dyn Read) -> Result<JsonFile> {
162162
serde_json::from_reader(reader).map_err(Error::Json)
163163
}
164164

@@ -171,7 +171,7 @@ fn compile(args: &Arguments) -> Result<()> {
171171

172172
// transform the IR into a Map of BPFPrograms
173173
let bpf_data: HashMap<String, BpfProgram> = compiler
174-
.compile_blob(filters, args.is_basic)
174+
.compile_blob(filters.0, args.is_basic)
175175
.map_err(Error::FileFormat)?;
176176

177177
// serialize the BPF programs & output them to a file
@@ -681,6 +681,25 @@ mod tests {
681681
.to_string();
682682
let json_input = unsafe { json_input.as_bytes_mut() };
683683
assert!(parse_json(&mut json_input.as_ref()).is_err());
684+
685+
// duplicate filter keys
686+
let mut json_input = r#"
687+
{
688+
"thread_1": {
689+
"default_action": "trap",
690+
"filter_action": "allow",
691+
"filter": []
692+
},
693+
"thread_1": {
694+
"default_action": "trap",
695+
"filter_action": "allow",
696+
"filter": []
697+
}
698+
}
699+
"#
700+
.to_string();
701+
let json_input = unsafe { json_input.as_bytes_mut() };
702+
assert!(parse_json(&mut json_input.as_ref()).is_err());
684703
}
685704

686705
// test with correctly formed JSON
@@ -689,7 +708,7 @@ mod tests {
689708
let mut json_input = "{}".to_string();
690709
let json_input = unsafe { json_input.as_bytes_mut() };
691710

692-
assert_eq!(parse_json(&mut json_input.as_ref()).unwrap().len(), 0);
711+
assert_eq!(parse_json(&mut json_input.as_ref()).unwrap().0.len(), 0);
693712

694713
// empty Filter
695714
let mut json_input =
@@ -757,6 +776,7 @@ mod tests {
757776

758777
let mut v2: Vec<_> = parse_json(&mut json_input.as_ref())
759778
.unwrap()
779+
.0
760780
.into_iter()
761781
.collect();
762782
v2.sort_by(|x, y| x.0.cmp(&y.0));

0 commit comments

Comments
 (0)