Skip to content

Commit d2077eb

Browse files
authored
fix: Detect invalid ICC profile tags (#4557)
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 #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 <[email protected]>
1 parent 904df59 commit d2077eb

File tree

8 files changed

+48
-7
lines changed

8 files changed

+48
-7
lines changed

src/jpeg.imageio/jpeginput.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,9 @@ JpgInput::read_icc_profile(j_decompress_ptr cinfo, ImageSpec& spec)
437437
std::string errormsg;
438438
bool ok = decode_icc_profile(icc_buf, spec, errormsg);
439439
if (!ok) {
440-
// errorfmt("Could not decode ICC profile: {}\n", errormsg);
441-
// return false;
442-
// Nah, just skip an ICC specific error?
440+
errorfmt("Possible corrupt file, could not decode ICC profile: {}\n",
441+
errormsg);
442+
return false;
443443
}
444444

445445
return true;

src/libOpenImageIO/icc.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,13 @@ decode_icc_profile(cspan<uint8_t> iccdata, ImageSpec& spec, std::string& error)
304304
if (!extract(iccdata, offset, tag, error))
305305
return false;
306306
string_view signature(tag.signature, 4);
307-
if (tag.offset + tag.size > iccdata.size()) {
308-
error = format("ICC profile tag {} extends past end", signature);
309-
return false;
307+
if (!check_span(iccdata, iccdata.data() + tag.offset,
308+
std::max(4U, tag.size))) {
309+
error = format(
310+
"ICC profile tag {} appears to contain corrupted/invalid data",
311+
signature);
312+
return false; // Non-sensical: tag extends beyond icc data block
310313
}
311-
312314
string_view typesignature((const char*)iccdata.data() + tag.offset, 4);
313315
// Strutil::print(" tag {} type {} offset {} size {}\n", signature,
314316
// typesignature, tag.offset, tag.size);
@@ -360,6 +362,14 @@ decode_icc_profile(cspan<uint8_t> iccdata, ImageSpec& spec, std::string& error)
360362
// Strutil::print(
361363
// "eng len={} stfoffset={} ({:x}) wcharsize={}\n", len,
362364
// stroffset, tag.offset + stroffset, sizeof(wchar_t));
365+
if (!check_span(iccdata,
366+
iccdata.data() + tag.offset + stroffset,
367+
len)) {
368+
error = format(
369+
"ICC profile tag {} appears to contain corrupted/invalid data",
370+
signature);
371+
return false; // Non-sensical: tag extends beyond icc data block
372+
}
363373
const char* start = (const char*)iccdata.data() + tag.offset
364374
+ stroffset;
365375
// The actual data is UTF-16

testsuite/jpeg-corrupt/ref/out-alt.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
1818
YResolution: 300
1919
jpeg:subsampling: "4:2:0"
2020
oiio:ColorSpace: "sRGB"
21+
corrupt-icc-4551.jpg
22+
DCT coefficient (lossy) or spatial difference (lossless) out of range

testsuite/jpeg-corrupt/ref/out-alt2.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
1818
YResolution: 300
1919
jpeg:subsampling: "4:2:0"
2020
oiio:ColorSpace: "sRGB"
21+
corrupt-icc-4551.jpg
22+
DCT coefficient (lossy) or spatial difference (lossless) out of range
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Reading src/corrupt-exif.jpg
2+
src/corrupt-exif.jpg : 256 x 256, 3 channel, uint8 jpeg
3+
SHA-1: 3EA7BAEB0871E9B9BAADF286521423199ACA2401
4+
channel list: R, G, B
5+
Orientation: 1 (normal)
6+
ResolutionUnit: "in"
7+
XResolution: 300
8+
YResolution: 300
9+
jpeg:subsampling: "4:2:0"
10+
oiio:ColorSpace: "sRGB"
11+
Reading src/corrupt-exif-1626.jpg
12+
src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
13+
SHA-1: 6CBB7DB602A67DB300AB28842F20F6DCA02B82B1
14+
channel list: R, G, B
15+
Orientation: 1 (normal)
16+
ResolutionUnit: "in"
17+
XResolution: 300
18+
YResolution: 300
19+
jpeg:subsampling: "4:2:0"
20+
oiio:ColorSpace: "sRGB"
21+
corrupt-icc-4551.jpg

testsuite/jpeg-corrupt/ref/out.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ src/corrupt-exif-1626.jpg : 256 x 256, 3 channel, uint8 jpeg
1818
YResolution: 300
1919
jpeg:subsampling: "4:2:0"
2020
oiio:ColorSpace: "sRGB"
21+
corrupt-icc-4551.jpg
22+
DCT coefficient (lossy) or spatial difference (lossless) out of range

testsuite/jpeg-corrupt/run.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,7 @@
1717
# nonsensical length, that before being fixed, caused a buffer overrun.
1818
command += info_command ("src/corrupt-exif-1626.jpg", safematch=True)
1919

20+
# This file has a corrupted ICC profile block that has tags that say they
21+
# extend beyond the boundaries of the ICC block itself.
22+
command += run_app (oiiotool("--echo corrupt-icc-4551.jpg"))
23+
command += run_app (oiio_app("iconvert") + " src/corrupt-icc-4551.jpg out-4551.jpg")
40 KB
Loading

0 commit comments

Comments
 (0)