Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ private static void extractFromTiff(RandomAccessRead reader,
{
int tag = readshort(endianess, reader);
int type = readshort(endianess, reader);
int count = readlong(endianess, reader);
int count = (int) readlong(endianess, reader);
int val;
// Note that when the type is shorter than 4 bytes, the rest can be garbage
// and must be ignored. E.g. short (2 bytes) from "01 00 38 32" (little endian)
Expand All @@ -322,7 +322,7 @@ private static void extractFromTiff(RandomAccessRead reader,
reader.read();
break;
default: // long and other types
val = readlong(endianess, reader);
val = (int) readlong(endianess, reader);
break;
}
switch (tag)
Expand Down Expand Up @@ -472,13 +472,14 @@ private static int readshort(char endianess, RandomAccessRead raf) throws IOExce
return (raf.read() << 8) | raf.read();
}

private static int readlong(char endianess, RandomAccessRead raf) throws IOException
private static long readlong(char endianess, RandomAccessRead raf) throws IOException
{
// TIFF LONG is an unsigned 32-bit value; mask so it widens correctly
if (endianess == 'I')
{
return raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24);
return (raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24)) & 0xFFFFFFFFL;
}
return (raf.read() << 24) | (raf.read() << 16) | (raf.read() << 8) | raf.read();
return ((raf.read() << 24) | (raf.read() << 16) | (raf.read() << 8) | raf.read()) & 0xFFFFFFFFL;
}

private static final byte[] fliptable = new byte[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;

Expand All @@ -34,6 +35,8 @@

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.RandomAccessRead;
import org.apache.pdfbox.io.RandomAccessReadBuffer;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
Expand Down Expand Up @@ -316,4 +319,44 @@ void testFillOrder2() throws IOException, URISyntaxException
assertEquals(1, document.getNumberOfPages());
}
}

/**
* Tests that CCITTFactory's private readlong() reads a TIFF LONG as an unsigned 32-bit
* value. The previous implementation returned a (possibly negative) int, which was then
* sign-extended when widened to long, corrupting IFD offsets/counts whose high bit is set
* (e.g. 0x80000000 and above).
*/
@Test
void testReadLongIsUnsigned() throws Exception
{
Method readLongMethod =
CCITTFactory.class.getDeclaredMethod("readlong", char.class, RandomAccessRead.class);
readLongMethod.setAccessible(true);

// all bits set: 0xFFFFFFFF == 4294967295 as an unsigned TIFF LONG.
// The buggy code returned the int -1, which as a long is -1, not 4294967295.
byte[] allOnes = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
assertReadLongUnsigned(readLongMethod, 'I', allOnes, 0xFFFFFFFFL);
assertReadLongUnsigned(readLongMethod, 'M', allOnes, 0xFFFFFFFFL);

// only the top bit set, in each byte order: 0x80000000 == 2147483648 unsigned.
// The buggy code returned the int Integer.MIN_VALUE, which sign-extends to a
// large negative long instead of 2147483648.
byte[] littleEndianTopBit = { 0x00, 0x00, 0x00, (byte) 0x80 };
assertReadLongUnsigned(readLongMethod, 'I', littleEndianTopBit, 0x80000000L);

byte[] bigEndianTopBit = { (byte) 0x80, 0x00, 0x00, 0x00 };
assertReadLongUnsigned(readLongMethod, 'M', bigEndianTopBit, 0x80000000L);
}

private static void assertReadLongUnsigned(Method readLongMethod, char endianess, byte[] bytes,
long expected) throws Exception
{
try (RandomAccessRead raf = new RandomAccessReadBuffer(bytes))
{
long value = (long) readLongMethod.invoke(null, endianess, raf);
assertEquals(expected, value);
assertTrue(value >= 0, "TIFF LONG must be read as unsigned, not sign-extended");
}
}
}