Don't allow deserializing names which shouldn't exist

This addresses the potential security issue discussed in the last patch
by implementing custom versions of `deserialize` that check for objects
corresponding to each name.
This commit is contained in:
Eric Kidd 2018-01-01 15:23:06 -05:00
parent 53d7c8ef3a
commit e7d13ed7d1
2 changed files with 147 additions and 85 deletions

View File

@ -128,6 +128,49 @@ impl<T> IndexMap<T> {
// Note that this does the right thing because we use `&self`.
self.into_iter()
}
/// Custom deserialization routine.
///
/// We will allocate an underlying array no larger than `max_entry_space` to
/// hold the data, so the maximum index must be less than `max_entry_space`.
/// This prevents mallicious *.wasm files from having a single entry with
/// the index `u32::MAX`, which would consume far too much memory.
///
/// The `deserialize_value` function will be passed the index of the value
/// being deserialized, and must deserialize the value.
pub fn deserialize_with<R, F>(
max_entry_space: usize,
deserialize_value: &F,
rdr: &mut R,
) -> Result<IndexMap<T>, Error>
where
R: Read,
F: Fn(u32, &mut R) -> Result<T, Error>,
{
let len: u32 = VarUint32::deserialize(rdr)?.into();
let mut map = IndexMap::with_capacity(len as usize);
let mut prev_idx = None;
for _ in 0..len {
let idx: u32 = VarUint32::deserialize(rdr)?.into();
if idx as usize >= max_entry_space {
return Err(Error::Other("index is larger than expected"));
}
match prev_idx {
Some(prev) if prev >= idx => {
// Supposedly these names must be "sorted by index", so
// let's try enforcing that and seeing what happens.
return Err(Error::Other("indices are out of order"));
}
_ => {
prev_idx = Some(idx);
}
}
let val = deserialize_value(idx, rdr)?;
map.insert(idx, val);
}
Ok(map)
}
}
impl<T: Clone> Clone for IndexMap<T> {
@ -286,34 +329,22 @@ where
}
}
impl<T: Deserialize> Deserialize for IndexMap<T>
impl<T: Deserialize> IndexMap<T>
where
T: Deserialize,
Error: From<<T as Deserialize>::Error>,
{
type Error = Error;
fn deserialize<R: Read>(rdr: &mut R) -> Result<Self, Self::Error> {
// SECURITY: Implemented wasted space checking.
let len: u32 = VarUint32::deserialize(rdr)?.into();
let mut map = IndexMap::with_capacity(len as usize);
let mut prev_idx = None;
for _ in 0..len {
let idx: u32 = VarUint32::deserialize(rdr)?.into();
match prev_idx {
Some(prev) if prev >= idx => {
// Supposedly these names must be "sorted by index", so
// let's try enforcing that and seeing what happens.
return Err(Error::Other("indices are out of order"));
}
_ => {
prev_idx = Some(idx);
}
}
let val = T::deserialize(rdr)?;
map.insert(idx, val);
}
Ok(map)
/// Deserialize a map containing simple values that support `Deserialize`.
/// We will allocate an underlying array no larger than `max_entry_space` to
/// hold the data, so the maximum index must be less than `max_entry_space`.
pub fn deserialize<R: Read>(
max_entry_space: usize,
rdr: &mut R,
) -> Result<Self, Error> {
let deserialize_value: fn(u32, &mut R) -> Result<T, Error> = |_idx, rdr| {
T::deserialize(rdr).map_err(Error::from)
};
Self::deserialize_with(max_entry_space, &deserialize_value, rdr)
}
}
@ -513,7 +544,7 @@ mod tests {
.expect("serialize failed");
let mut input = io::Cursor::new(&output);
let deserialized = IndexMap::deserialize(&mut input).expect("deserialize failed");
let deserialized = IndexMap::deserialize(2, &mut input).expect("deserialize failed");
assert_eq!(deserialized, map);
}
@ -527,7 +558,7 @@ mod tests {
"val 0".to_string().serialize(&mut valid).unwrap();
VarUint32::from(1u32).serialize(&mut valid).unwrap();
"val 1".to_string().serialize(&mut valid).unwrap();
let map = IndexMap::<String>::deserialize(&mut io::Cursor::new(valid))
let map = IndexMap::<String>::deserialize(2, &mut io::Cursor::new(valid))
.expect("unexpected error deserializing");
assert_eq!(map.len(), 2);
@ -538,7 +569,18 @@ mod tests {
"val 1".to_string().serialize(&mut invalid).unwrap();
VarUint32::from(0u32).serialize(&mut invalid).unwrap();
"val 0".to_string().serialize(&mut invalid).unwrap();
let res = IndexMap::<String>::deserialize(&mut io::Cursor::new(invalid));
let res = IndexMap::<String>::deserialize(2, &mut io::Cursor::new(invalid));
assert!(res.is_err());
}
#[test]
fn deserialize_enforces_max_idx() {
// Build an example with an out-of-bounds index by hand.
let mut invalid = vec![];
VarUint32::from(1u32).serialize(&mut invalid).unwrap();
VarUint32::from(5u32).serialize(&mut invalid).unwrap();
"val 5".to_string().serialize(&mut invalid).unwrap();
let res = IndexMap::<String>::deserialize(1, &mut io::Cursor::new(invalid));
assert!(res.is_err());
}
}

View File

@ -1,6 +1,6 @@
use std::io::{Read, Write};
use super::{Deserialize, Error, Serialize, VarUint32, VarUint7};
use super::{Deserialize, Error, Module, Serialize, VarUint32, VarUint7};
use super::index_map::IndexMap;
const NAME_TYPE_MODULE: u8 = 0;
@ -28,6 +28,31 @@ pub enum NameSection {
},
}
impl NameSection {
/// Deserialize a name section.
pub fn deserialize<R: Read>(
module: &Module,
rdr: &mut R,
) -> Result<NameSection, Error> {
let name_type: u8 = VarUint7::deserialize(rdr)?.into();
let name_payload_len: u32 = VarUint32::deserialize(rdr)?.into();
let name_section = match name_type {
NAME_TYPE_MODULE => NameSection::Module(ModuleNameSection::deserialize(rdr)?),
NAME_TYPE_FUNCTION => NameSection::Function(FunctionNameSection::deserialize(module, rdr)?),
NAME_TYPE_LOCAL => NameSection::Local(LocalNameSection::deserialize(module, rdr)?),
_ => {
let mut name_payload = vec![0u8; name_payload_len as usize];
rdr.read_exact(&mut name_payload)?;
NameSection::Unparsed {
name_type,
name_payload,
}
}
};
Ok(name_section)
}
}
impl Serialize for NameSection {
type Error = Error;
@ -60,29 +85,6 @@ impl Serialize for NameSection {
}
}
impl Deserialize for NameSection {
type Error = Error;
fn deserialize<R: Read>(rdr: &mut R) -> Result<NameSection, Error> {
let name_type: u8 = VarUint7::deserialize(rdr)?.into();
let name_payload_len: u32 = VarUint32::deserialize(rdr)?.into();
let name_section = match name_type {
NAME_TYPE_MODULE => NameSection::Module(ModuleNameSection::deserialize(rdr)?),
NAME_TYPE_FUNCTION => NameSection::Function(FunctionNameSection::deserialize(rdr)?),
NAME_TYPE_LOCAL => NameSection::Local(LocalNameSection::deserialize(rdr)?),
_ => {
let mut name_payload = vec![0u8; name_payload_len as usize];
rdr.read_exact(&mut name_payload)?;
NameSection::Unparsed {
name_type,
name_payload,
}
}
};
Ok(name_section)
}
}
/// The name of this module.
#[derive(Clone, Debug, PartialEq)]
pub struct ModuleNameSection {
@ -139,6 +141,19 @@ impl FunctionNameSection {
pub fn names_mut(&mut self) -> &mut NameMap {
&mut self.names
}
/// Deserialize names, making sure that all names correspond to functions.
pub fn deserialize<R: Read>(
module: &Module,
rdr: &mut R,
) -> Result<FunctionNameSection, Error> {
let funcs = module.function_section().ok_or_else(|| {
Error::Other("cannot deserialize names without a function section")
})?;
let max_entry_space = funcs.entries().len();
let names = IndexMap::deserialize(max_entry_space, rdr)?;
Ok(FunctionNameSection { names })
}
}
impl Serialize for FunctionNameSection {
@ -149,15 +164,6 @@ impl Serialize for FunctionNameSection {
}
}
impl Deserialize for FunctionNameSection {
type Error = Error;
fn deserialize<R: Read>(rdr: &mut R) -> Result<FunctionNameSection, Error> {
let names = IndexMap::deserialize(rdr)?;
Ok(FunctionNameSection { names })
}
}
/// The names of the local variables in this module's functions.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LocalNameSection {
@ -175,7 +181,33 @@ impl LocalNameSection {
pub fn local_names_mut(&mut self) -> &mut IndexMap<NameMap> {
&mut self.local_names
}
}
/// Deserialize names, making sure that all names correspond to local
/// variables.
pub fn deserialize<R: Read>(
module: &Module,
rdr: &mut R,
) -> Result<LocalNameSection, Error> {
let funcs = module.function_section().ok_or_else(|| {
Error::Other("cannot deserialize local names without a function section")
})?;
let max_entry_space = funcs.entries().len();
let code = module.code_section().ok_or_else(|| {
Error::Other("cannot deserialize local names without a code section")
})?;
let deserialize_locals = |idx: u32, rdr: &mut R| {
let max_local_entry_space = code.bodies()[idx as usize].locals().len();
IndexMap::deserialize(max_local_entry_space, rdr)
};
let local_names = IndexMap::deserialize_with(
max_entry_space,
&deserialize_locals,
rdr,
)?;
Ok(LocalNameSection { local_names })
}}
impl Serialize for LocalNameSection {
type Error = Error;
@ -185,15 +217,6 @@ impl Serialize for LocalNameSection {
}
}
impl Deserialize for LocalNameSection {
type Error = Error;
fn deserialize<R: Read>(rdr: &mut R) -> Result<LocalNameSection, Error> {
let local_names = IndexMap::deserialize(rdr)?;
Ok(LocalNameSection { local_names })
}
}
/// A map from indices to names.
pub type NameMap = IndexMap<String>;
@ -205,48 +228,45 @@ mod tests {
// A helper funtion for the tests. Serialize a section, deserialize it,
// and make sure it matches the original.
fn serialize_and_deserialize(original: NameSection) {
fn serialize_test(original: NameSection) -> Vec<u8> {
let mut buffer = vec![];
original
.clone()
.serialize(&mut buffer)
.expect("serialize error");
let mut input = Cursor::new(buffer);
let deserialized = NameSection::deserialize(&mut input).expect("deserialize error");
assert_eq!(deserialized, original);
buffer
}
#[test]
fn serialize_and_deserialize_module_name() {
let sect = ModuleNameSection::new("my_mod");
serialize_and_deserialize(NameSection::Module(sect));
fn serialize_module_name() {
let original = NameSection::Module(ModuleNameSection::new("my_mod"));
serialize_test(original.clone());
}
#[test]
fn serialize_and_deserialize_function_names() {
fn serialize_function_names() {
let mut sect = FunctionNameSection::default();
sect.names_mut().insert(0, "hello_world".to_string());
serialize_and_deserialize(NameSection::Function(sect));
serialize_test(NameSection::Function(sect));
}
#[test]
fn serialize_and_deserialize_local_names() {
fn serialize_local_names() {
let mut sect = LocalNameSection::default();
let mut locals = NameMap::default();
locals.insert(0, "msg".to_string());
sect.local_names_mut().insert(0, locals);
serialize_and_deserialize(NameSection::Local(sect));
serialize_test(NameSection::Local(sect));
}
#[test]
fn serialize_and_deserialize_unparsed() {
let sect = NameSection::Unparsed {
let original = NameSection::Unparsed {
// A made-up name section type which is unlikely to be allocated
// soon, in order to allow us to test `Unparsed`.
name_type: 120,
name_payload: vec![0u8, 1, 2],
};
serialize_and_deserialize(sect);
serialize_test(original.clone());
}
#[test]
@ -261,7 +281,7 @@ mod tests {
match *s {
Section::Custom(ref cs) if cs.name() == "name" => {
let mut cursor = Cursor::new(cs.payload());
let section = NameSection::deserialize(&mut cursor)
let section = NameSection::deserialize(&module, &mut cursor)
.expect("could not parse name section");
if let NameSection::Function(function_names) = section {
function_names_opt = Some(function_names);