Support reading up to 8 IFDs.
This commit is contained in:
parent
b9575efd0b
commit
8e9ff50ca8
|
@ -199,7 +199,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_field() {
|
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();
|
let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
|
||||||
match reader.get_field(Tag::ImageDescription, In(0)).unwrap().value {
|
match reader.get_field(Tag::ImageDescription, In(0)).unwrap().value {
|
||||||
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test image"]),
|
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"]),
|
Value::Ascii(ref vec) => assert_eq!(vec, &[b"Test thumbnail"]),
|
||||||
ref v => panic!("wrong variant {:?}", v)
|
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]
|
#[test]
|
||||||
fn display_value_with_unit() {
|
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();
|
let reader = Reader::new(&mut BufReader::new(&file)).unwrap();
|
||||||
// No unit.
|
// No unit.
|
||||||
let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
let exifver = reader.get_field(Tag::ExifVersion, In::PRIMARY).unwrap();
|
||||||
|
@ -222,7 +226,7 @@ mod tests {
|
||||||
// Fixed string.
|
// Fixed string.
|
||||||
let width = reader.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
|
let width = reader.get_field(Tag::ImageWidth, In::PRIMARY).unwrap();
|
||||||
assert_eq!(width.display_value().with_unit(&reader).to_string(),
|
assert_eq!(width.display_value().with_unit(&reader).to_string(),
|
||||||
"15 pixels");
|
"17 pixels");
|
||||||
// Unit tag (with a non-default value).
|
// Unit tag (with a non-default value).
|
||||||
let gpsalt = reader.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
|
let gpsalt = reader.get_field(Tag::GPSAltitude, In::PRIMARY).unwrap();
|
||||||
assert_eq!(gpsalt.display_value().with_unit(&reader).to_string(),
|
assert_eq!(gpsalt.display_value().with_unit(&reader).to_string(),
|
||||||
|
|
52
src/tiff.rs
52
src/tiff.rs
|
@ -111,16 +111,27 @@ fn parse_exif_sub<E>(data: &[u8])
|
||||||
if E::loadu16(data, 2) != TIFF_FORTY_TWO {
|
if E::loadu16(data, 2) != TIFF_FORTY_TWO {
|
||||||
return Err(Error::InvalidFormat("Invalid 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();
|
let mut fields = Vec::new();
|
||||||
parse_ifd::<E>(&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::<E>(
|
||||||
|
&mut fields, data, ifd_offset, Context::Tiff, ifd_num)?;
|
||||||
|
ifd_num_ck = ifd_num.checked_add(1);
|
||||||
|
}
|
||||||
Ok(fields)
|
Ok(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse IFD [EXIF23 4.6.2].
|
// Parse IFD [EXIF23 4.6.2].
|
||||||
fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||||
offset: usize, ctx: Context, ifd_num: u16)
|
offset: usize, ctx: Context, ifd_num: u16)
|
||||||
-> Result<(), Error> where E: Endian {
|
-> Result<usize, Error> where E: Endian {
|
||||||
// Count (the number of the entries).
|
// Count (the number of the entries).
|
||||||
if data.len() < offset || data.len() - offset < 2 {
|
if data.len() < offset || data.len() - offset < 2 {
|
||||||
return Err(Error::InvalidFormat("Truncated IFD count"));
|
return Err(Error::InvalidFormat("Truncated IFD count"));
|
||||||
|
@ -172,14 +183,7 @@ fn parse_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||||
return Err(Error::InvalidFormat("Truncated next IFD offset"));
|
return Err(Error::InvalidFormat("Truncated next IFD offset"));
|
||||||
}
|
}
|
||||||
let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize;
|
let next_ifd_offset = E::loadu32(data, offset + 2 + count * 12) as usize;
|
||||||
// Ignore IFDs after IFD1 (thumbnail) for now.
|
Ok(next_ifd_offset)
|
||||||
if next_ifd_offset == 0 || ifd_num > 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
if ctx != Context::Tiff {
|
|
||||||
return Err(Error::InvalidFormat("Unexpected next IFD"));
|
|
||||||
}
|
|
||||||
parse_ifd::<E>(fields, data, next_ifd_offset, Context::Tiff, 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_child_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
fn parse_child_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||||
|
@ -190,7 +194,10 @@ fn parse_child_ifd<'a, E>(fields: &mut Vec<Field<'a>>, data: &'a [u8],
|
||||||
// element of the field.
|
// element of the field.
|
||||||
let ofs = pointer.get_uint(0).ok_or(
|
let ofs = pointer.get_uint(0).ok_or(
|
||||||
Error::InvalidFormat("Invalid pointer"))? as usize;
|
Error::InvalidFormat("Invalid pointer"))? as usize;
|
||||||
parse_ifd::<E>(fields, data, ofs, ctx, ifd_num)
|
match parse_ifd::<E>(fields, data, ofs, ctx, ifd_num)? {
|
||||||
|
0 => Ok(()),
|
||||||
|
_ => Err(Error::InvalidFormat("Unexpected next IFD")),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_tiff(buf: &[u8]) -> bool {
|
pub fn is_tiff(buf: &[u8]) -> bool {
|
||||||
|
@ -455,16 +462,25 @@ mod tests {
|
||||||
assert_eq!(format!("{:^10}", In(65535)), " IFD65535 ");
|
assert_eq!(format!("{:^10}", In(65535)), " IFD65535 ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before the error is returned, the IFD is parsed twice as the
|
// Before the error is returned, the IFD is parsed multiple times
|
||||||
// 0th and 1st IFDs.
|
// as the 0th, 1st, ..., and n-th IFDs.
|
||||||
#[test]
|
#[test]
|
||||||
fn inf_loop_by_next() {
|
fn inf_loop_by_next() {
|
||||||
let data = b"MM\0\x2a\0\0\0\x08\
|
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";
|
\0\x01\x01\0\0\x03\0\0\0\x01\0\x14\0\0\0\0\0\x08";
|
||||||
// assert_err_pat!(parse_exif(data),
|
assert_err_pat!(parse_exif(data),
|
||||||
// Error::InvalidFormat("Unexpected next IFD"));
|
Error::InvalidFormat("Limit the IFD count to 8"));
|
||||||
let (v, _) = parse_exif(data).unwrap();
|
}
|
||||||
assert_eq!(v.len(), 2);
|
|
||||||
|
#[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]
|
#[test]
|
||||||
|
|
BIN
tests/unit.tif
BIN
tests/unit.tif
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue