Skip to content

IFD tag reads are limited by decoding_buffer_size #413

Description

@nuri-yoo

I ran into this through the image crate while reading an ICC profile from a small TIFF. The profile is present in the file, but icc_profile() returns Ok(None).

At the tiff layer, reading an IFD tag fails with LimitsExceeded whenever the tag is larger than decoding_buffer_size, even if it is well below ifd_value_size. This reproduces on current main (e27f6fe).

Here is a small reproducer. It writes an 8x8 RGB image (192 bytes of pixel data) with a 16 KiB ICC profile, then sets decoding_buffer_size to the image size. ifd_value_size is left at its default of 1 MiB.

use std::io::Cursor;
use tiff::decoder::{Decoder, Limits};
use tiff::encoder::{colortype, TiffEncoder};
use tiff::tags::Tag;

fn main() {
    const WIDTH: u32 = 8;
    const HEIGHT: u32 = 8;
    const IMAGE_BYTES: usize = (WIDTH * HEIGHT * 3) as usize;

    let icc: Vec<u8> = (0..16 * 1024).map(|i| i as u8).collect();
    let mut file = Cursor::new(Vec::new());
    {
        let mut encoder = TiffEncoder::new(&mut file).unwrap();
        let mut image = encoder
            .new_image::<colortype::RGB8>(WIDTH, HEIGHT)
            .unwrap();
        image
            .encoder()
            .write_tag(Tag::IccProfile, &icc[..])
            .unwrap();
        image.write_data(&[0; IMAGE_BYTES]).unwrap();
    }

    let mut limits = Limits::default();
    limits.decoding_buffer_size = IMAGE_BYTES;

    file.set_position(0);
    let mut decoder = Decoder::open(&mut file).unwrap().with_limits(limits);
    decoder.next_image().unwrap();

    let result = decoder
        .current_ifd()
        .get_tag_u8_vec(Tag::IccProfile);
    println!("{result:?}");
}

The result is:

Err(LimitsExceeded)

I would expect this read to succeed because the 16 KiB value fits within ifd_value_size. find_tag_buf fails in the same way.

The two checks that appear to cause this are in src/decoder/ifd.rs:

  • Entry::buffer_with_capacity checks the raw tag byte count against decoding_buffer_size.
  • Entry::vec_with_capacity checks the expanded Value representation against decoding_buffer_size.

The first check looks like it should use ifd_value_size, whose documentation describes it as the maximum size of a single IFD value. For the expanded Value allocation, intermediate_buffer_size seems like the matching limit; using ifd_value_size there would reduce the effective limit by size_of::<Value>() compared with the current behavior.

This matters for callers that size decoding_buffer_size from the decoded image. A perfectly ordinary metadata value can be larger than the pixels of a thumbnail or other small image, so the tag becomes unreadable even though the configured IFD limit allows it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions