#393: Input size validation of ICC profiles

This commit is contained in:
Harald Kuhr 2017-11-18 15:54:27 +01:00
parent 719b6e7883
commit cd9f3a036e
3 changed files with 45 additions and 13 deletions

View File

@ -65,7 +65,6 @@ final class KCMSSanitizerStrategy implements ICCProfileSanitizer {
((array[index + 3] & 0xff) ); ((array[index + 3] & 0xff) );
} }
// TODO: Move to some common util // TODO: Move to some common util
static void intToBigEndian(final int value, final byte[] array, final int index) { static void intToBigEndian(final int value, final byte[] array, final int index) {
array[index ] = (byte) (value >> 24); array[index ] = (byte) (value >> 24);

View File

@ -689,11 +689,20 @@ public final class JPEGImageReader extends ImageReaderBase {
return profile; return profile;
} }
static void intToBigEndian(int value, byte[] array, int index) { // TODO: Move to some common util
array[index] = (byte) (value >> 24); static int intFromBigEndian(final byte[] array, final int index) {
array[index+1] = (byte) (value >> 16); return ((array[index ] & 0xff) << 24) |
array[index+2] = (byte) (value >> 8); ((array[index + 1] & 0xff) << 16) |
array[index+3] = (byte) (value); ((array[index + 2] & 0xff) << 8) |
((array[index + 3] & 0xff) );
}
// TODO: Move to some common util
static void intToBigEndian(final int value, final byte[] array, final int index) {
array[index ] = (byte) (value >> 24);
array[index + 1] = (byte) (value >> 16);
array[index + 2] = (byte) (value >> 8);
array[index + 3] = (byte) (value );
} }
private void initHeader() throws IOException { private void initHeader() throws IOException {
@ -853,7 +862,10 @@ public final class JPEGImageReader extends ImageReaderBase {
return null; return null;
} }
return readICCProfileSafe(stream, allowBadIndexes); int iccChunkDataSize = segment.data.length - segment.identifier.length() - 3; // ICC_PROFILE + null + chunk number + count
int iccSize = intFromBigEndian(segment.data, segment.identifier.length() + 3);
return readICCProfileSafe(stream, allowBadIndexes, iccSize, iccChunkDataSize);
} }
else if (!segments.isEmpty()) { else if (!segments.isEmpty()) {
// NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order... // NOTE: This is probably over-complicated, as I've never encountered ICC_PROFILE chunks out of order...
@ -888,25 +900,40 @@ public final class JPEGImageReader extends ImageReaderBase {
InputStream[] streams = new InputStream[count]; InputStream[] streams = new InputStream[count];
streams[badICC ? 0 : chunkNumber - 1] = stream; streams[badICC ? 0 : chunkNumber - 1] = stream;
int iccChunkDataSize = 0;
int iccSize = 0;
for (int i = 1; i < count; i++) { for (int i = 1; i < count; i++) {
stream = new DataInputStream(segments.get(i).data()); Application segment = segments.get(i);
stream = new DataInputStream(segment.data());
chunkNumber = stream.readUnsignedByte(); chunkNumber = stream.readUnsignedByte();
if (!badICC && stream.readUnsignedByte() != chunkCount) { if (stream.readUnsignedByte() != chunkCount && !badICC) {
throw new IIOException(String.format("Bad number of 'ICC_PROFILE' chunks: %d of %d.", chunkNumber, chunkCount)); throw new IIOException(String.format("Bad number of 'ICC_PROFILE' chunks: %d of %d.", chunkNumber, chunkCount));
} }
streams[badICC ? i : chunkNumber - 1] = stream; int index = badICC ? i : chunkNumber - 1;
streams[index] = stream;
iccChunkDataSize += segment.data.length - segment.identifier.length() - 3;
if (index == 0) {
iccSize = intFromBigEndian(segment.data, segment.identifier.length() + 3);
}
} }
return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes); return readICCProfileSafe(new SequenceInputStream(Collections.enumeration(Arrays.asList(streams))), allowBadIndexes, iccSize, iccChunkDataSize);
} }
return null; return null;
} }
private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile) throws IOException { private ICC_Profile readICCProfileSafe(final InputStream stream, final boolean allowBadProfile, final int iccSize, final int iccChunkDataSize) throws IOException {
if (iccSize < 0 || iccSize > iccChunkDataSize) {
processWarningOccurred(String.format("Truncated 'ICC_PROFILE' chunk(s), size: %d. Ignoring ICC profile.", iccSize));
return null;
}
try { try {
ICC_Profile profile = ICC_Profile.getInstance(stream); ICC_Profile profile = ICC_Profile.getInstance(stream);

View File

@ -2205,6 +2205,13 @@ public final class TIFFImageReader extends ImageReaderBase {
if (entry != null) { if (entry != null) {
byte[] value = (byte[]) entry.getValue(); byte[] value = (byte[]) entry.getValue();
// Validate ICC profile size vs actual value size
int size = (value[0] & 0xff) << 24 | (value[1] & 0xff) << 16 | (value[2] & 0xff) << 8 | (value[3] & 0xff);
if (size < 0 || size > value.length) {
processWarningOccurred("Ignoring truncated ICC profile: Bad ICC profile size (" + size + ")");
return null;
}
try { try {
// WEIRDNESS: Reading profile from InputStream is somehow more compatible // WEIRDNESS: Reading profile from InputStream is somehow more compatible
// than reading from byte array (chops off extra bytes + validates profile). // than reading from byte array (chops off extra bytes + validates profile).
@ -2218,7 +2225,6 @@ public final class TIFFImageReader extends ImageReaderBase {
return null; return null;
} }
@Override @Override
public boolean canReadRaster() { public boolean canReadRaster() {
return true; return true;