mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 01:47:08 -06:00
security: bound RAW linked-directory metadata reads
This commit is contained in:
@@ -74,12 +74,13 @@ pub fn embedded_previews(bytes: &[u8]) -> Vec<Preview<'_>> {
|
||||
if let (Some(off), Some(len)) = (t.tag_uint(&ifd, tag::JPEG_INTERCHANGE_FORMAT), t.tag_uint(&ifd, tag::JPEG_INTERCHANGE_FORMAT_LENGTH)) {
|
||||
found.extend(candidate(bytes, off as usize, len as usize));
|
||||
}
|
||||
if matches!(t.tag_uint(&ifd, tag::COMPRESSION), Some(6 | 7)) {
|
||||
let offs = t.tag_uints(&ifd, tag::STRIP_OFFSETS);
|
||||
let lens = t.tag_uints(&ifd, tag::STRIP_BYTE_COUNTS);
|
||||
if let ([off], [len]) = (offs.as_slice(), lens.as_slice()) {
|
||||
found.extend(candidate(bytes, *off as usize, *len as usize));
|
||||
}
|
||||
if matches!(t.tag_uint(&ifd, tag::COMPRESSION), Some(6 | 7))
|
||||
&& let (Some(offsets), Some(lengths)) = (ifd.get(tag::STRIP_OFFSETS), ifd.get(tag::STRIP_BYTE_COUNTS))
|
||||
&& offsets.count == 1
|
||||
&& lengths.count == 1
|
||||
&& let (Some(off), Some(len)) = (t.uint(offsets, 0), t.uint(lengths, 0))
|
||||
{
|
||||
found.extend(candidate(bytes, off as usize, len as usize));
|
||||
}
|
||||
// Panasonic RW2 JpgFromRaw (0x002E) holds a complete JPEG.
|
||||
if let Some(e) = ifd.get(0x002E).filter(|e| e.typ == 7)
|
||||
|
||||
+22
-3
@@ -213,9 +213,11 @@ impl<'a> Tiff<'a> {
|
||||
}
|
||||
for t in [tag::SUB_IFDS, tag::EXIF_IFD] {
|
||||
if let Some(e) = ifd.get(t) {
|
||||
// Reverse so the first SubIFD is visited first.
|
||||
let mut v = self.uints(e);
|
||||
v.truncate(16);
|
||||
// Linked-directory traversal only needs the first sixteen offsets. Read
|
||||
// those values directly instead of expanding the complete file-controlled
|
||||
// array and truncating it afterward.
|
||||
let n = (e.count as usize).min(16);
|
||||
let v = (0..n).map(|i| self.uint(e, i)).collect::<Option<Vec<_>>>().unwrap_or_default();
|
||||
stack.extend(v.into_iter().rev().map(|o| o as usize));
|
||||
}
|
||||
}
|
||||
@@ -450,3 +452,20 @@ mod tests {
|
||||
assert!(out.contains("output truncated") || out.len() < 1 << 20);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_directory_values_are_read_only_as_needed() {
|
||||
// The traversal limit is independent of the TIFF entry count: callers should
|
||||
// never need a complete attacker-controlled offset array to visit SubIFDs.
|
||||
let mut b = b"II*\0\x08\0\0\0".to_vec();
|
||||
b.extend_from_slice(&1u16.to_le_bytes());
|
||||
b.extend_from_slice(&330u16.to_le_bytes());
|
||||
b.extend_from_slice(&4u16.to_le_bytes());
|
||||
b.extend_from_slice(&(1_000_000u32).to_le_bytes());
|
||||
b.extend_from_slice(&26u32.to_le_bytes());
|
||||
b.extend_from_slice(&0u32.to_le_bytes());
|
||||
b.extend_from_slice(&0u32.to_le_bytes());
|
||||
b.resize(26 + 16 * 4, 0);
|
||||
let t = Tiff::new(&b).unwrap();
|
||||
assert!(t.all_ifds().len() <= 17);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user