fix: beef up Exif error detection (#5322)

One small improvement in how decode_exif detects corruption, to ensure
that the initial header examination doesn't read past the array bounds
-- by starting off with a check that the exif blob's size is at least a
big as a TIFFHeader, otherwise parsing it is pointless.

Then the rest of the changes are finding places where readers call
decode_exif but ignore the results, effectively ignoring corrupt Exif
blobs. That is in fact the default intended behavior (if the rest of the
image file can be read properly), but we are supposed to be checking the
global "imageinput:strict" attribute and considering it a hard error
when in strict mode. There were several places where we didn't handle
that case, in the JPEG, PNG, WebP, and Heif readers.

---------

Signed-off-by: Larry Gritz <lg@larrygritz.com>
This commit is contained in:
Larry Gritz
2026-07-15 23:41:20 -07:00
committed by GitHub
parent a9a170244c
commit c5e085eee3
8 changed files with 43 additions and 12 deletions
+10 -2
View File
@@ -412,12 +412,20 @@ HeifInput::seek_subimage(int subimage, int miplevel)
if (Strutil::iequals(m_ihandle.get_metadata_type(m), "Exif")
&& metacontents.size() >= 10) {
cspan<uint8_t> s(&metacontents[10], metacontents.size() - 10);
decode_exif(s, m_spec);
bool ok = decode_exif(s, m_spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
errorfmt("Could not decode Exif");
return false;
}
} else if (0 // For now, skip this, I haven't seen anything useful
&& Strutil::iequals(m_ihandle.get_metadata_type(m), "mime")
&& Strutil::iequals(m_ihandle.get_metadata_content_type(m),
"application/rdf+xml")) {
decode_xmp(metacontents, m_spec);
bool ok = decode_xmp(metacontents, m_spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
errorfmt("Could not decode XMP");
return false;
}
} else {
#ifdef DEBUG
print(
+7 -2
View File
@@ -296,8 +296,13 @@ JpgInput::open(const std::string& name, ImageSpec& newspec)
if (is_exif_marker(m)) {
// The block starts with "Exif\0\0", so skip 6 bytes to get
// to the start of the actual Exif data TIFF directory
decode_exif(string_view((char*)m->data + 6, m->data_length - 6),
m_spec);
bool ok = decode_exif(string_view((char*)m->data + 6,
m->data_length - 6),
m_spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
errorfmt("Could not decode Exif");
return false;
}
} else if (m->marker == (JPEG_APP0 + 1) && m->data_length >= 28
&& !strncmp((const char*)m->data,
"http://ns.adobe.com/xap/1.0/", 28)) { //NOSONAR
+2
View File
@@ -1233,6 +1233,8 @@ decode_exif(cspan<uint8_t> exif, ImageSpec& spec)
// N.B. Just read libtiff's "tiff.h" for info on the structure
// layout of TIFF headers and directory entries. The TIFF spec
// itself is also helpful in this area.
if (exif.size() < sizeof(TIFFHeader))
return false;
TIFFHeader head = *(const TIFFHeader*)exif.data();
if (head.tiff_magic != 0x4949 && head.tiff_magic != 0x4d4d)
return false;
+14 -3
View File
@@ -171,7 +171,7 @@ decode_png_text_exif(string_view raw, ImageSpec& spec)
raw.remove_prefix(2);
}
if (Strutil::istarts_with(decoded, "Exif")) {
decode_exif(decoded, spec);
return decode_exif(decoded, spec);
}
return false;
}
@@ -282,7 +282,12 @@ read_info(png_structp& sp, png_infop& ip, int& bit_depth, int& color_type,
// Most PNG files seem to encode Exif by cramming it into a text
// field, with the key "Raw profile type exif" and then a special
// text encoding that we handle with the following function:
decode_png_text_exif(text_ptr[i].text, spec);
bool ok = decode_png_text_exif(text_ptr[i].text, spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
ImageInput* pnginput = (ImageInput*)png_get_io_ptr(sp);
pnginput->errorfmt("Could not decode Exif");
return false;
}
} else {
spec.attribute(text_ptr[i].key, text_ptr[i].text);
}
@@ -345,7 +350,13 @@ read_info(png_structp& sp, png_infop& ip, int& bit_depth, int& color_type,
png_uint_32 num_exif = 0;
png_bytep exif_data = nullptr;
if (png_get_eXIf_1(sp, ip, &num_exif, &exif_data)) {
decode_exif(cspan<uint8_t>(exif_data, span_size_t(num_exif)), spec);
bool ok = decode_exif(cspan<uint8_t>(exif_data, span_size_t(num_exif)),
spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
ImageInput* pnginput = (ImageInput*)png_get_io_ptr(sp);
pnginput->errorfmt("Could not decode Exif");
return false;
}
}
#endif
+1 -1
View File
@@ -1353,7 +1353,7 @@ PSDInput::load_resource_1058(uint32_t length)
if (!decode_exif(data, m_composite_attribs)
|| !decode_exif(data, m_common_attribs)) {
errorfmt("Failed to decode Exif data");
errorfmt("Could not decode Exif");
return false;
}
return true;
+7 -2
View File
@@ -207,8 +207,13 @@ WebpInput::open(const std::string& name, ImageSpec& spec,
if (m_demux_flags & EXIF_FLAG
&& WebPDemuxGetChunk(m_demux, "EXIF", 1, &chunk_iter)) {
cspan<uint8_t> exif_span(chunk_iter.chunk.bytes, chunk_iter.chunk.size);
if (webp_exif_payload_has_tiff_header(exif_span))
decode_exif(exif_span, m_spec);
if (webp_exif_payload_has_tiff_header(exif_span)) {
bool ok = decode_exif(exif_span, m_spec);
if (!ok && OIIO::get_int_attribute("imageinput:strict")) {
errorfmt("Could not decode Exif");
return false;
}
}
WebPDemuxReleaseChunkIterator(&chunk_iter);
}
if (m_demux_flags & XMP_FLAG
+1 -1
View File
@@ -2090,7 +2090,7 @@ src/Layers_32bit_RGB.psd : 48 x 27, 3 channel, float psd
stEvt:instanceID: "xmp.iid:68fcb000-4377-c148-974d-bdd193ca024d; xmp.iid:cbd904c5-53b4-0d4e-9ff8-37ac35429f46"
stEvt:softwareAgent: "Adobe Photoshop 23.3 (Windows)"
stEvt:when: "2024-04-01T19:35:16+02:00"
oiiotool ERROR: read : Failed to decode Exif data
oiiotool ERROR: read : Could not decode Exif
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
Full command line was:
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd
+1 -1
View File
@@ -2090,7 +2090,7 @@ src/Layers_32bit_RGB.psd : 48 x 27, 3 channel, float psd
stEvt:instanceID: "xmp.iid:68fcb000-4377-c148-974d-bdd193ca024d; xmp.iid:cbd904c5-53b4-0d4e-9ff8-37ac35429f46"
stEvt:softwareAgent: "Adobe Photoshop 23.3 (Windows)"
stEvt:when: "2024-04-01T19:35:16+02:00"
oiiotool ERROR: read : Failed to decode Exif data
oiiotool ERROR: read : Could not decode Exif
failed to open "src/crash-psd-exif-1632.psd": failed load_resources
Full command line was:
> oiiotool --info -v -a --hash src/crash-psd-exif-1632.psd