Tim Scheckenbach created TIKA-4830:
--------------------------------------
Summary: JackcessParser.parse leaks Jackcess RuntimeExceptions
(IndexOutOfBoundsException / IllegalStateException) on malformed MDBs while
reading the global usage map
Key: TIKA-4830
URL: https://issues.apache.org/jira/browse/TIKA-4830
Project: Tika
Issue Type: Bug
Components: parser
Affects Versions: 4.0.0
Reporter: Tim Scheckenbach
Attachments: inputs.zip
{{JackcessParser.parse}} is declared {{{}throws IOException, SAXException,
TikaException{}}}. Two fuzz inputs for the same open path —
{{DatabaseBuilder.open}} → {{PageChannel.initialize}} → {{UsageMap.read}}
(global usage map, page 1, row 0)
— make Jackcess throw a {{RuntimeException}} that Tika does not wrap. The
OSS-Fuzz target {{JackcessParserFuzzer}} swallows only
{{{}IllegalArgumentException | TikaException | SAXException | IOException{}}},
so both are fuzzer crashes. {{AutoDetectParser}} / {{CompositeParser}} would
wrap them as {{{}TikaException: Unexpected RuntimeException from
JackcessParser{}}}; callers of {{JackcessParser}} directly do not.
Calling
{code:java}
new JackcessParser().parse(TikaInputStream.get(new
File("crash-083680cb14186e1be3e26b93946ea43c538d2e62")),
new ToTextContentHandler(), new Metadata(), new ParseContext());
{code}
on the first attached file (32768-byte Jet 4 MDB) results in:
{code:java}
java.lang.IndexOutOfBoundsException
at java.base/java.nio.Buffer.checkIndex(Buffer.java:743)
at java.base/java.nio.HeapByteBuffer.get(HeapByteBuffer.java:169)
at com.healthmarketscience.jackcess.impl.UsageMap.read(UsageMap.java:133)
at
com.healthmarketscience.jackcess.impl.PageChannel.initialize(PageChannel.java:117)
at
com.healthmarketscience.jackcess.impl.DatabaseImpl.<init>(DatabaseImpl.java:598)
at
com.healthmarketscience.jackcess.impl.DatabaseImpl.open(DatabaseImpl.java:458)
at
com.healthmarketscience.jackcess.DatabaseBuilder.open(DatabaseBuilder.java:278)
at
org.apache.tika.parser.microsoft.JackcessParser.parse(JackcessParser.java:101)
{code}
{{UsageMap.read}} is declared {{throws IOException}} but does:
{code:java}
short rowStart = TableImpl.findRowStart(tableBuffer, rowNum, format);
int rowEnd = TableImpl.findRowEnd(tableBuffer, rowNum, format);
tableBuffer.limit(rowEnd);
byte mapType = tableBuffer.get(rowStart); // IndexOutOfBoundsException
{code}
Tika does not catch {{IndexOutOfBoundsException}} at all.
Calling the same parse on the second attached file,
{{crash-107db845b1fe3fee58a3b1da3a4646e604af329b}} (24576-byte Jet 4 MDB),
results in:
{code:java}
java.lang.IllegalStateException: invalid page number 169285950
at
com.healthmarketscience.jackcess.impl.PageChannel.validatePageNumber(PageChannel.java:203)
at
com.healthmarketscience.jackcess.impl.PageChannel.readPage(PageChannel.java:219)
at
com.healthmarketscience.jackcess.impl.TempPageHolder.setPage(TempPageHolder.java:86)
at
com.healthmarketscience.jackcess.impl.UsageMap$ReferenceHandler.<init>(UsageMap.java:724)
at
com.healthmarketscience.jackcess.impl.UsageMap.initHandler(UsageMap.java:146)
at com.healthmarketscience.jackcess.impl.UsageMap.read(UsageMap.java:135)
at
com.healthmarketscience.jackcess.impl.PageChannel.initialize(PageChannel.java:117)
at
com.healthmarketscience.jackcess.impl.DatabaseImpl.open(DatabaseImpl.java:458)
at
com.healthmarketscience.jackcess.DatabaseBuilder.open(DatabaseBuilder.java:278)
at
org.apache.tika.parser.microsoft.JackcessParser.parse(JackcessParser.java:101)
{code}
Here the global usage map is a _reference_ map ({{{}MAP_TYPE_REFERENCE{}}}).
Its constructor walks page pointers and {{validatePageNumber}} is declared
{{throws IOException}} but does:
{code:java}
if ((pageNumber <= INVALID_PAGE_NUMBER) || (pageNumber >= nextPageNumber)) {
throw new IllegalStateException("invalid page number " + pageNumber);
}
{code}
Page 169285950 is far past the end of a 6-page file. Tika already catches
{{{}IllegalStateException{}}}, but only to map "Incorrect password"; every
other one is rethrown:
{code:java}
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("Incorrect
password")) {
throw new EncryptedDocumentException(e);
}
throw e;
}
{code}
Suggested Tika-side fix: catch {{RuntimeException}} from
{{DatabaseBuilder.open}} (or at least {{IndexOutOfBoundsException}} and
remaining {{{}IllegalStateException{}}}) and rethrow as {{TikaException}} or
{{{}CorruptedFileException{}}}. One catch covers both inputs. The Jackcess-side
fix is to throw {{IOException}} for a corrupt usage map, matching the declared
contracts of {{UsageMap.read}} and {{{}validatePageNumber{}}}.
I have attached both files as an archive to the issue.
Found by the CISPA Fandango Team
--
This message was sent by Atlassian Jira
(v8.20.10#820010)