JDK-8186334 : JarFile throws ArrayIndexOutOfBoundsException when the manifest contains certain characters
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.jar
  • Affected Version: 9
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2017-08-14
  • Updated: 2017-12-14
  • Resolved: 2017-08-22
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10
10 b21Fixed
Related Reports
Relates :  
Sub Tasks
JDK-8186552 :  
Description
FULL PRODUCT VERSION :
$ java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+180)
Java HotSpot(TM) 64-Bit Server VM (build 9+180, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
15.6.0 Darwin Kernel Version 15.6.0: Tue Apr 11 16:00:51 PDT 2017; root:xnu-3248.60.11.5.3~1/RELEASE_X86_64 x86_64
Ubuntu 14.04.5 LTS (via Travis build)

A DESCRIPTION OF THE PROBLEM :
ServiceLoader fails with ArrayIndexOutOfBoundsException when certain jars are on the classpath. The main one I'm aware of is jackson-jr-objects-2.9.0.jar [1]. This jar and provided test code works find when using java 8. Even if it is some problem with the jar it seems like the error message should be more clear as to what is corrupt or incorrect instead of throwing an ArrayIndexOutOfBoundsException.

Details included here, but you can also refer to the jackson issue [2].

[1]: http://jcenter.bintray.com/com/fasterxml/jackson/jr/jackson-jr-objects/2.9.0/jackson-jr-objects-2.9.0.jar
[2]: https://github.com/FasterXML/jackson-jr/issues/55

REGRESSION.  Last worked in version 8u152

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Sample program:

```
import java.util.Iterator;
import java.util.ServiceLoader;

public class Test {
  public static void main(String[] args) throws Exception {
    ServiceLoader<String> loader = ServiceLoader.load(String.class);
    Iterator<String> iter = loader.iterator();
    while (iter.hasNext()) {
      System.out.println(iter.next());
    }
  }
}
```

Run with java 9 and include jackson-jr-objects-2.9.0.jar on the classpath:

```
$ java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+180)
Java HotSpot(TM) 64-Bit Server VM (build 9+180, mixed mode)
$ java Test
$ java -classpath .:jackson-core-2.9.0.jar Test
$ java -classpath .:jackson-core-2.9.0.jar:jackson-jr-objects-2.9.0.jar Test
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 64
	at java.base/java.util.jar.JarFile.match(JarFile.java:941)
	at java.base/java.util.jar.JarFile.checkForSpecialAttributes(JarFile.java:971)
	at java.base/java.util.jar.JarFile.isMultiRelease(JarFile.java:366)
	at java.base/java.util.jar.JarFile.getEntry(JarFile.java:491)
	at java.base/java.util.jar.JarFile.getJarEntry(JarFile.java:447)
	at java.base/jdk.internal.util.jar.JarIndex.getJarIndex(JarIndex.java:115)
	at java.base/jdk.internal.loader.URLClassPath$JarLoader$1.run(URLClassPath.java:692)
	at java.base/jdk.internal.loader.URLClassPath$JarLoader$1.run(URLClassPath.java:684)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/jdk.internal.loader.URLClassPath$JarLoader.ensureOpen(URLClassPath.java:683)
	at java.base/jdk.internal.loader.URLClassPath$JarLoader.<init>(URLClassPath.java:658)
	at java.base/jdk.internal.loader.URLClassPath$3.run(URLClassPath.java:426)
	at java.base/jdk.internal.loader.URLClassPath$3.run(URLClassPath.java:409)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/jdk.internal.loader.URLClassPath.getLoader(URLClassPath.java:408)
	at java.base/jdk.internal.loader.URLClassPath.getLoader(URLClassPath.java:377)
	at java.base/jdk.internal.loader.URLClassPath.access$000(URLClassPath.java:84)
	at java.base/jdk.internal.loader.URLClassPath$1.next(URLClassPath.java:270)
	at java.base/jdk.internal.loader.URLClassPath$1.hasMoreElements(URLClassPath.java:281)
	at java.base/jdk.internal.loader.BuiltinClassLoader$1.hasNext(BuiltinClassLoader.java:356)
	at java.base/jdk.internal.loader.BuiltinClassLoader$1.hasMoreElements(BuiltinClassLoader.java:364)
	at java.base/java.lang.CompoundEnumeration.next(ClassLoader.java:2921)
	at java.base/java.lang.CompoundEnumeration.hasMoreElements(ClassLoader.java:2930)
	at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1197)
	at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1215)
	at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1259)
	at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1294)
	at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1379)
	at Test.main(Test.java:9)
```

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Runs without throwing an error.
ACTUAL -
Fails with ArrayIndexOutOfBoundsException

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.Iterator;
import java.util.ServiceLoader;

public class Test {
  public static void main(String[] args) throws Exception {
    ServiceLoader<String> loader = ServiceLoader.load(String.class);
    Iterator<String> iter = loader.iterator();
    while (iter.hasNext()) {
      System.out.println(iter.next());
    }
  }
}
---------- END SOURCE ----------


Comments
The Jackson maintainers have worked around this bug: https://github.com/FasterXML/jackson-jr/pull/56.
18-08-2017

Seems like I did an off-by-one logical error for the case when a manifest contains "`", which could be resolved in many ways, e.g., by adding a 65th element to the *LASTOCC byte arrays. diff -r a50527268122 src/java.base/share/classes/java/util/jar/JarFile.java --- a/src/java.base/share/classes/java/util/jar/JarFile.java Tue Aug 01 08:56:42 2017 -0700 +++ b/src/java.base/share/classes/java/util/jar/JarFile.java Fri Aug 18 00:43:07 2017 +0200 @@ -848,7 +848,7 @@ private static final byte[] MULTIRELEASE_OPTOSFT; static { - CLASSPATH_LASTOCC = new byte[64]; + CLASSPATH_LASTOCC = new byte[65]; CLASSPATH_OPTOSFT = new byte[12]; CLASSPATH_LASTOCC[(int)'C' - 32] = 1; CLASSPATH_LASTOCC[(int)'L' - 32] = 2; @@ -865,7 +865,7 @@ } CLASSPATH_OPTOSFT[11] = 1; - MULTIRELEASE_LASTOCC = new byte[64]; + MULTIRELEASE_LASTOCC = new byte[65]; MULTIRELEASE_OPTOSFT = new byte[19]; MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1; MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
17-08-2017

ServiceLoader is not required to reproduce this issue. It also happens when attempting to load a class from the .jar using Class.forName(). Run the attached test with: java -classpath .:./jackson-jr-objects-2.9.0.jar TestForName
17-08-2017

Looks like this was introduced by JDK-8152733 in 9b113, which made some changes to the JarFile.match() method.
17-08-2017

To reproduce the issue, run the attached test case. Following are the results : JDK 8u144 - Pass JDK 9-ea+180 - Fail Following is the output on JDK 9ea+180: C:\>java -classpath .;D:\jackson-jr-objects-2.9.0.jar JI9050488 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 64 at java.base/java.util.jar.JarFile.match(JarFile.java:941) at java.base/java.util.jar.JarFile.checkForSpecialAttributes(JarFile.java:971) at java.base/java.util.jar.JarFile.isMultiRelease(JarFile.java:366) at java.base/java.util.jar.JarFile.getEntry(JarFile.java:491) at java.base/java.util.jar.JarFile.getJarEntry(JarFile.java:447) at java.base/jdk.internal.util.jar.JarIndex.getJarIndex(JarIndex.java:115) at java.base/jdk.internal.loader.URLClassPath$JarLoader$1.run(URLClassPath.java:692) at java.base/jdk.internal.loader.URLClassPath$JarLoader$1.run(URLClassPath.java:684) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.base/jdk.internal.loader.URLClassPath$JarLoader.ensureOpen(URLClassPath.java:683) at java.base/jdk.internal.loader.URLClassPath$JarLoader.<init>(URLClassPath.java:658) at java.base/jdk.internal.loader.URLClassPath$3.run(URLClassPath.java:426) at java.base/jdk.internal.loader.URLClassPath$3.run(URLClassPath.java:409) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.base/jdk.internal.loader.URLClassPath.getLoader(URLClassPath.java:408) at java.base/jdk.internal.loader.URLClassPath.getLoader(URLClassPath.java:377) at java.base/jdk.internal.loader.URLClassPath.access$000(URLClassPath.java:84) at java.base/jdk.internal.loader.URLClassPath$1.next(URLClassPath.java:270) at java.base/jdk.internal.loader.URLClassPath$1.hasMoreElements(URLClassPath.java:281) at java.base/jdk.internal.loader.BuiltinClassLoader$1.hasNext(BuiltinClassLoader.java:356) at java.base/jdk.internal.loader.BuiltinClassLoader$1.hasMoreElements(BuiltinClassLoader.java:364) at java.base/java.lang.CompoundEnumeration.next(ClassLoader.java:2921) at java.base/java.lang.CompoundEnumeration.hasMoreElements(ClassLoader.java:2930) at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.nextProviderClass(ServiceLoader.java:1197) at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNextService(ServiceLoader.java:1215) at java.base/java.util.ServiceLoader$LazyClassPathLookupIterator.hasNext(ServiceLoader.java:1259) at java.base/java.util.ServiceLoader$2.hasNext(ServiceLoader.java:1294) at java.base/java.util.ServiceLoader$3.hasNext(ServiceLoader.java:1379) at JI9050488.main(JI9050488.java:8)
17-08-2017