Skip to content

Commit

Permalink
fix: Detect invalid ICC profile tags (AcademySoftwareFoundation#4557)
Browse files Browse the repository at this point in the history
Affects jpeg, tiff, and other formats with embedded ICC profiles.
Corrupted/invalid tag offset and/or length values were causing us to
touch memory outside the memory allocated for the profile.

Fixes AcademySoftwareFoundation#4551

Not sure if this is wise or not, but for JPEGs, I also enabled some
previously commented-out code that has the effect of treating a file
with a verifiably corrupted/nonsensical ICC profile block as a failed
file. If people ever complain about this in the future, maybe we'll come
back and add some kind of global option that lets the app express how
tolerant OIIO readers should be about corruptions in files -- do you
assume that the whole file is bogus or malicious upon discovering the
first invalidity, or do you press on and hope you can get something else
useful from the pixels? Let's be cautious for now.

---------

Signed-off-by: Larry Gritz <lg@larrygritz.com>
  • Loading branch information
lgritz committed Dec 8, 2024
1 parent 6a6eda8 commit 17f4b42
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 7 deletions.
6 changes: 3 additions & 3 deletions src/jpeg.imageio/jpeginput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,9 @@ JpgInput::read_icc_profile(j_decompress_ptr cinfo, ImageSpec& spec)
std::string errormsg;
bool ok = decode_icc_profile(icc_buf, spec, errormsg);
if (!ok) {
// errorfmt("Could not decode ICC profile: {}\n", errormsg);
// return false;
// Nah, just skip an ICC specific error?
errorfmt("Possible corrupt file, could not decode ICC profile: {}\n",
errormsg);
return false;
}

return true;
Expand Down
18 changes: 14 additions & 4 deletions src/libOpenImageIO/icc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,13 @@ decode_icc_profile(cspan<uint8_t> iccdata, ImageSpec& spec, std::string& error)
if (!extract(iccdata, offset, tag, error))
return false;
string_view signature(tag.signature, 4);
if (tag.offset + tag.size > iccdata.size()) {
error = format("ICC profile tag {} extends past end", signature);
return false;
if (!check_span(iccdata, iccdata.data() + tag.offset,
std::max(4U, tag.size))) {
error = format(
"ICC profile tag {} appears to contain corrupted/invalid data",
signature);
return false; // Non-sensical: tag extends beyond icc data block
}

string_view typesignature((const char*)iccdata.data() + tag.offset, 4);
// Strutil::print(" tag {} type {} offset {} size {}\n", signature,
// typesignature, tag.offset, tag.size);
Expand Down Expand Up @@ -360,6 +362,14 @@ decode_icc_profile(cspan<uint8_t> iccdata, ImageSpec& spec, std::string& error)
// Strutil::print(
// "eng len={} stfoffset={} ({:x}) wcharsize={}\n", len,
// stroffset, tag.offset + stroffset, sizeof(wchar_t));
if (!check_span(iccdata,
iccdata.data() + tag.offset + stroffset,
len)) {
error = format(
"ICC profile tag {} appears to contain corrupted/invalid data",
signature);
return false; // Non-sensical: tag extends beyond icc data block
}
const char* start = (const char*)iccdata.data() + tag.offset
+ stroffset;
// The actual data is UTF-16
Expand Down
2 changes: 2 additions & 0 deletions testsuite/jpeg-corrupt/ref/out-alt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
YResolution: 300
jpeg:subsampling: "4:2:0"
oiio:ColorSpace: "sRGB"
corrupt-icc-4551.jpg
DCT coefficient (lossy) or spatial difference (lossless) out of range
2 changes: 2 additions & 0 deletions testsuite/jpeg-corrupt/ref/out-alt2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
YResolution: 300
jpeg:subsampling: "4:2:0"
oiio:ColorSpace: "sRGB"
corrupt-icc-4551.jpg
DCT coefficient (lossy) or spatial difference (lossless) out of range
21 changes: 21 additions & 0 deletions testsuite/jpeg-corrupt/ref/out-alt3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Reading src/corrupt-exif.jpg
src/corrupt-exif.jpg : 256 x 256, 3 channel, uint8 jpeg
SHA-1: 3EA7BAEB0871E9B9BAADF286521423199ACA2401
channel list: R, G, B
Orientation: 1 (normal)
ResolutionUnit: "in"
XResolution: 300
YResolution: 300
jpeg:subsampling: "4:2:0"
oiio:ColorSpace: "sRGB"
Reading src/corrupt-exif-1626.jpg
src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
SHA-1: 6CBB7DB602A67DB300AB28842F20F6DCA02B82B1
channel list: R, G, B
Orientation: 1 (normal)
ResolutionUnit: "in"
XResolution: 300
YResolution: 300
jpeg:subsampling: "4:2:0"
oiio:ColorSpace: "sRGB"
corrupt-icc-4551.jpg
2 changes: 2 additions & 0 deletions testsuite/jpeg-corrupt/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
YResolution: 300
jpeg:subsampling: "4:2:0"
oiio:ColorSpace: "sRGB"
corrupt-icc-4551.jpg
DCT coefficient (lossy) or spatial difference (lossless) out of range
4 changes: 4 additions & 0 deletions testsuite/jpeg-corrupt/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
# nonsensical length, that before being fixed, caused a buffer overrun.
command += info_command ("src/corrupt-exif-1626.jpg", safematch=True)

# This file has a corrupted ICC profile block that has tags that say they
# extend beyond the boundaries of the ICC block itself.
command += run_app (oiiotool("--echo corrupt-icc-4551.jpg"))
command += run_app (oiio_app("iconvert") + " src/corrupt-icc-4551.jpg out-4551.jpg")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 17f4b42

Please sign in to comment.