From 8e9ff50ca8f3ed6558dcbe6c93c759f82d8190cd Mon Sep 17 00:00:00 2001 From: KAMADA Ken'ichi Date: Wed, 8 May 2019 23:09:03 +0900 Subject: [PATCH] Support reading up to 8 IFDs. --- src/reader.rs | 10 ++++++--- src/tiff.rs | 52 +++++++++++++++++++++++++++++---------------- tests/unit.tif | Bin 691 -> 0 bytes tests/yaminabe.tif | Bin 0 -> 541 bytes 4 files changed, 41 insertions(+), 21 deletions(-) delete mode 100644 tests/unit.tif create mode 100644 tests/yaminabe.tif 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 4fbf6df679f10d50b72c81dcb8e90f278247f295..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 691 zcmb7=yAAu6h2nM;9&%NA%(Yja!TQbcYBzde2OK8t126mtcECxo#hEo{frgElN&_jdIE)6VVXBz(`~QD? z=FSK9ZPPYvFi3f~Ds=USj7AN&;ATyZD@^8MO8cy2kDj?OQzMM|s>2orNe814fxEl~ z8-Ce_@Wya=ZCoMM*mZFa+js6GEHU@mPb*c+M+yihTs`FA)sZOG@zNlrVP&fV1H*KY y=m$VQK;nWQ=#Y0n{15E%l8n;aq`bt;9FY5