RFR: 8242882: opening jar file with large manifest might throw NegativeArraySizeException [v2]
Brent Christian
bchristi at openjdk.java.net
Wed Sep 30 17:36:10 UTC 2020
On Tue, 29 Sep 2020 11:39:20 GMT, Jaikiran Pai <jpai at openjdk.org> wrote:
>> Can I please get a review and a sponsor for a fix for https://bugs.openjdk.java.net/browse/JDK-8242882?
>>
>> As noted in that JBS issue, if the size of the Manifest entry in the jar happens to be very large (such that it exceeds
>> the `Integer.MAX_VALUE`), then the current code in `JarFile#getBytes` can lead to a `NegativeArraySizeException`. This
>> is due to the: if (len != -1 && len <= 65535) block which evaluates to `true` when the size of the manifest entry is
>> larger than `Integer.MAX_VALUE`. As a result, this then ends up calling the code which can lead to the
>> `NegativeArraySizeException`. The commit in this PR fixes that issue by changing those `if/else` blocks to prevent
>> this issue and instead use a code path that leads to the `InputStream#readAllBytes()` which internally has the
>> necessary checks to throw the expected `OutOfMemoryError`. This commit also includes a jtreg test case which
>> reproduces the issue and verifies the fix.
>
> Jaikiran Pai has updated the pull request incrementally with one additional commit since the last revision:
>
> Address the review comments and introduce an array size check in JarFile.getBytes() method itself
If there is only modest improvement in test duration, we may want to add a jtreg timeout tag. Also, given the long
duration but relative low priority of testing this, it perhaps should be moved out of Tier 1. I will look into those
things after your next update.
src/java.base/share/classes/java/util/jar/JarFile.java line 161:
> 159: * OutOfMemoryError: Required array size too large
> 160: */
> 161: private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
"/**" comments are generally only used for public documentation. For use here, probably a single line // comment would
be sufficient to explain what this value is.
This constant is also named, "MAX_ARRAY_SIZE" in various places, which seems more applicable to this case.
src/java.base/share/classes/java/util/jar/JarFile.java line 801:
> 799: if (len > MAX_BUFFER_SIZE) {
> 800: throw new OutOfMemoryError("Required array size too large");
> 801: }
I would just add a new `long zeSize` to read and check `ze.getSize()`, and then (int) cast it into `len`, as before.
Then I think no changes would be needed past L802, `int bytesRead;`
test/jdk/java/util/jar/JarFile/LargeManifestOOMTest.java line 45:
> 43: public class LargeManifestOOMTest {
> 44:
> 45:
Unneeded blank lines
test/jdk/java/util/jar/JarFile/LargeManifestOOMTest.java line 83:
> 81: }
> 82: }
> 83:
Extra line
test/jdk/java/util/jar/JarFile/LargeManifestOOMTest.java line 78:
> 76: bw.write("OOM-Test: ");
> 77: for (long i = 0; i < 2147483648L; i++) {
> 78: bw.write("a");
As you probably noticed, this test takes a little while to run. One way to speed it up a little would be to write more
characters at a time. While we're at it, we may as well make the Manifest well-formed by breaking it into 72-byte
lines. See "Line length" under:
https://docs.oracle.com/en/java/javase/15/docs/specs/jar/jar.html#notes-on-manifest-and-signature-files
Just write enough lines to exceed Integer.MAX_VALUE bytes.
-------------
Changes requested by bchristi (Reviewer).
PR: https://git.openjdk.java.net/jdk/pull/323
More information about the core-libs-dev
mailing list