diff options
| author | Edward Barnard | 2017-03-03 13:50:34 +0000 | 
|---|---|---|
| committer | Edward Barnard | 2017-03-03 13:53:19 +0000 | 
| commit | af6c771aca701c566920eac04141c16c2b7c6c91 (patch) | |
| tree | 11b0010fa332586d7f4e5519043bf728bb3ce5ad | |
| parent | 41e5fe9c3197da4ede35c1bfeaa21316d699848a (diff) | |
| download | rust-plist-af6c771aca701c566920eac04141c16c2b7c6c91.tar.bz2 | |
Limit individual allocation sizes to the size of the plist being read.
| -rw-r--r-- | src/binary/reader.rs | 67 | ||||
| -rw-r--r-- | tests/fuzzer.rs | 22 | 
2 files changed, 58 insertions, 31 deletions
| diff --git a/src/binary/reader.rs b/src/binary/reader.rs index c3c0be4..600d3b3 100644 --- a/src/binary/reader.rs +++ b/src/binary/reader.rs @@ -1,6 +1,7 @@  use byteorder::{BigEndian, ReadBytesExt};  use chrono::{Duration, TimeZone, UTC}; -use std::io::{Cursor, Read, Seek, SeekFrom}; +use std::io::{Read, Seek, SeekFrom}; +use std::mem;  use std::string::{FromUtf8Error, FromUtf16Error};  use {Error, Result, PlistEvent, u64_to_usize}; @@ -36,6 +37,9 @@ pub struct EventReader<R> {      reader: R,      ref_size: u8,      finished: bool, +    // The largest single allocation allowed for this Plist. +    // Equal to the number of bytes in the Plist minus the magic and trailer. +    max_allocation: u64,  }  impl<R: Read + Seek> EventReader<R> { @@ -46,6 +50,21 @@ impl<R: Read + Seek> EventReader<R> {              reader: reader,              ref_size: 0,              finished: false, +            max_allocation: 0 +        } +    } + +    fn can_allocate<T>(&self, len: u64) -> bool { +        let byte_len = len.saturating_mul(mem::size_of::<T>() as u64); +        byte_len <= self.max_allocation +    } + +    fn allocate_vec<T>(&self, len: u64) -> Result<Vec<T>> { +        if self.can_allocate::<T>(len) { +            let len = u64_to_usize(len)?; +            Ok(Vec::with_capacity(len)) +        } else { +            Err(Error::InvalidData)          }      } @@ -58,13 +77,17 @@ impl<R: Read + Seek> EventReader<R> {          }          // Trailer starts with 6 bytes of padding -        try!(self.reader.seek(SeekFrom::End(-32 + 6))); +        let trailer_start = try!(self.reader.seek(SeekFrom::End(-32 + 6))); +          let offset_size = try!(self.reader.read_u8());          self.ref_size = try!(self.reader.read_u8());          let num_objects = try!(self.reader.read_u64::<BigEndian>());          let top_object = try!(self.reader.read_u64::<BigEndian>());          let offset_table_offset = try!(self.reader.read_u64::<BigEndian>()); +        // File size minus trailer and header +        self.max_allocation = trailer_start.saturating_sub(6 + 8); +          // Read offset table          try!(self.reader.seek(SeekFrom::Start(offset_table_offset)));          self.object_offsets = try!(self.read_ints(num_objects, offset_size)); @@ -79,9 +102,7 @@ impl<R: Read + Seek> EventReader<R> {      }      fn read_ints(&mut self, len: u64, size: u8) -> Result<Vec<u64>> { -        let len = try!(u64_to_usize(len)); -        let mut ints = Vec::with_capacity(len); -        // TODO: Is the match hoisted out of the loop? +        let mut ints = self.allocate_vec(len)?;          for _ in 0..len {              match size {                  1 => ints.push(try!(self.reader.read_u8()) as u64), @@ -115,18 +136,10 @@ impl<R: Read + Seek> EventReader<R> {      }      fn read_data(&mut self, len: u64) -> Result<Vec<u8>> { -        let len = try!(u64_to_usize(len)); -        let mut data = vec![0; len]; -        let mut total_read = 0; - -        while total_read < len { -            let read = try!(self.reader.read(&mut data[total_read..])); -            if read == 0 { -                return Err(Error::UnexpectedEof); -            } -            total_read += read; -        } - +        let mut data = self.allocate_vec::<u8>(len)?; +        // Safe as u8 is a Copy type and we have already know len has been allocated. +        unsafe { data.set_len(len as usize) } +        self.reader.read_exact(&mut data)?;          Ok(data)      } @@ -226,16 +239,11 @@ impl<R: Read + Seek> EventReader<R> {              }              (0x6, n) => {                  // UTF-16 string -                // n is the length of 16 bit code units -                // len is the number of bytes -                let len = try!(self.read_object_len(n)) * 2; -                let raw = try!(self.read_data(len)); -                let mut cursor = Cursor::new(raw); +                let len_utf16_codepoints = try!(self.read_object_len(n)); +                let mut raw_utf16 = self.allocate_vec(len_utf16_codepoints)?; -                let len_div_2 = try!(u64_to_usize(len / 2)); -                let mut raw_utf16 = Vec::with_capacity(len_div_2); -                while cursor.position() < len { -                    raw_utf16.push(try!(cursor.read_u16::<BigEndian>())) +                for _ in 0..len_utf16_codepoints { +                    raw_utf16.push(try!(self.reader.read_u16::<BigEndian>()));                  }                  let string = try!(String::from_utf16(&raw_utf16)); @@ -261,11 +269,8 @@ impl<R: Read + Seek> EventReader<R> {                  let key_refs = try!(self.read_refs(len));                  let value_refs = try!(self.read_refs(len)); -                let len_mul_2 = try!(u64_to_usize(len * 2)); -                let len = try!(u64_to_usize(len)); - -                let mut object_refs = Vec::with_capacity(len_mul_2); - +                let mut object_refs = self.allocate_vec(len * 2)?; +                let len = key_refs.len();                  for i in 1..len + 1 {                      // Reverse so we can pop off the end of the stack in order                      object_refs.push(value_refs[len - i]); diff --git a/tests/fuzzer.rs b/tests/fuzzer.rs new file mode 100644 index 0000000..701df5e --- /dev/null +++ b/tests/fuzzer.rs @@ -0,0 +1,22 @@ +extern crate plist; + +use std::io::Cursor; +use plist::Plist; + +#[test] +fn too_large_allocation() { +    let data = b"bplist00\"&L^^^^^^^^-^^^^^^^^^^^"; +    test_fuzzer_data_err(data); +} + +#[test] +fn too_large_allocation_2() { +    let data = b"bplist00;<)\x9fX\x0a<h\x0a:hhhhG:hh\x0amhhhhhhx#hhT)\x0a*"; +    test_fuzzer_data_err(data); +} + +fn test_fuzzer_data_err(data: &[u8]) { +    let cursor = Cursor::new(data); +    let res = Plist::read(cursor); +    assert!(res.is_err()); +} | 
