diff --git a/src/reader.rs b/src/reader.rs index ae0cdfa..2b67d2c 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -199,7 +199,7 @@ mod tests { #[test] fn get_field() { - let file = File::open("tests/exif.tif").unwrap(); + let file = File::open("tests/yaminabe.tif").unwrap(); let reader = Reader::new(&mut BufReader::new(&file)).unwrap(); match reader.get_field(Tag::ImageDescription, In(0)).unwrap().value { Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]), @@ -209,11 +209,15 @@ mod tests { Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]), ref v => panic!("wrong variant {:?}", v) } + match reader.get_field(Tag::ImageDescription, In(2)).unwrap().value { + Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test 2nd IFD"]), + ref v => panic!("wrong variant {:?}", v) + } } #[test] fn display_value_with_unit() { - let file = File::open("tests/unit.tif").unwrap(); + let file = File::open("tests/yaminabe.tif").unwrap(); let reader = Reader::new(&mut BufReader::new(&file)).unwrap(); // No unit. let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap(); @@ -222,7 +226,7 @@ mod tests { // Fixed string. let width = reader.get_field(Tag::ImageWidth, In::PRIMARY).unwrap(); assert_eq!(width.display_value().with_unit(&reader).to_string(), - "15 pixels"); + "17 pixels"); // Unit tag (with a non-default value). let gpsalt = reader.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap(); assert_eq!(gpsalt.display_value().with_unit(&reader).to_string(), diff --git a/src/tiff.rs b/src/tiff.rs index 60da204..e098141 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -111,16 +111,27 @@ fn parse_exif_sub(data: &[u8]) if E::loadu16(data, 2) != TIFF_FORTY_TWO { return Err(Error::InvalidFormat("Invalid forty two")); } - let ifd_offset = E::loadu32(data, 4) as usize; + let mut ifd_offset = E::loadu32(data, 4) as usize; + let mut ifd_num_ck = Some(0); let mut fields = Vec::new(); - parse_ifd::(&mut fields, data, ifd_offset, Context::Tiff, 0)?; + while ifd_offset != 0 { + let ifd_num = ifd_num_ck.ok_or(Error::InvalidFormat("Too many IFDs"))?; + // Limit the number of IFDs to defend against resource exhaustion + // attacks. + if ifd_num >= 8 { + return Err(Error::InvalidFormat("Limit the IFD count to 8")); + } + ifd_offset = parse_ifd::( + &mut fields, data, ifd_offset, Context::Tiff, ifd_num)?; + ifd_num_ck = ifd_num.checked_add(1); + } Ok(fields) } // Parse IFD [EXIF23 4.6.2]. fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], offset: usize, ctx: Context, ifd_num: u16) - -> Result<(), Error> where E: Endian { + -> Result where E: Endian { // Count (the number of the entries). if data.len() < offset || data.len() - offset < 2 { return Err(Error::InvalidFormat("Truncated IFD count")); @@ -172,14 +183,7 @@ fn parse_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], return Err(Error::InvalidFormat("Truncated next IFD offset")); } let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize; - // Ignore IFDs after IFD1 (thumbnail) for now. - if next_ifd_offset == 0 || ifd_num > 0 { - return Ok(()); - } - if ctx != Context::Tiff { - return Err(Error::InvalidFormat("Unexpected next IFD")); - } - parse_ifd::(fields, data, next_ifd_offset, Context::Tiff, 1) + Ok(next_ifd_offset) } fn parse_child_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], @@ -190,7 +194,10 @@ fn parse_child_ifd<'a, E>(fields: &mut Vec>, data: &'a [u8], // element of the field. let ofs = pointer.get_uint(0).ok_or( Error::InvalidFormat("Invalid pointer"))? as usize; - parse_ifd::(fields, data, ofs, ctx, ifd_num) + match parse_ifd::(fields, data, ofs, ctx, ifd_num)? { + 0 => Ok(()), + _ => Err(Error::InvalidFormat("Unexpected next IFD")), + } } pub fn is_tiff(buf: &[u8]) -> bool { @@ -455,16 +462,25 @@ mod tests { assert_eq!(format!("{:^10}", In(65535)), " IFD65535 "); } - // Before the error is returned, the IFD is parsed twice as the - // 0th and 1st IFDs. + // Before the error is returned, the IFD is parsed multiple times + // as the 0th, 1st, ..., and n-th IFDs. #[test] fn inf_loop_by_next() { let data = b"MM\0\x2a\0\0\0\x08\ \0\x01\x01\0\0\x03\0\0\0\x01\0\x14\0\0\0\0\0\x08"; - // assert_err_pat!(parse_exif(data), - // Error::InvalidFormat("Unexpected next IFD")); - let (v, _) = parse_exif(data).unwrap(); - assert_eq!(v.len(), 2); + assert_err_pat!(parse_exif(data), + Error::InvalidFormat("Limit the IFD count to 8")); + } + + #[test] + fn inf_loop_by_exif_next() { + let data = b"MM\x00\x2a\x00\x00\x00\x08\ + \x00\x01\x87\x69\x00\x04\x00\x00\x00\x01\x00\x00\x00\x1a\ + \x00\x00\x00\x00\ + \x00\x01\x90\x00\x00\x07\x00\x00\x00\x040231\ + \x00\x00\x00\x08"; + assert_err_pat!(parse_exif(data), + Error::InvalidFormat("Unexpected next IFD")); } #[test] diff --git a/tests/unit.tif b/tests/unit.tif deleted file mode 100644 index 4fbf6df..0000000 Binary files a/tests/unit.tif and /dev/null differ diff --git a/tests/yaminabe.tif b/tests/yaminabe.tif new file mode 100644 index 0000000..b7d85ac Binary files /dev/null and b/tests/yaminabe.tif differ