8153213: Jar manifest attribute "Multi-Release" accepts any value
Reviewed-by: psandoz, redestad
Contributed-by: steve.drach@oracle.com
--- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue Apr 12 13:53:48 2016 +0800
+++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Tue Apr 12 18:25:10 2016 +0200
@@ -894,7 +894,8 @@
private static final byte[] CLASSPATH_LASTOCC;
private static final byte[] MULTIRELEASE_CHARS =
- {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':', ' '};
+ {'M','U','L','T','I','-','R','E','L','E', 'A', 'S', 'E', ':',
+ ' ', 'T', 'R', 'U', 'E'};
// The bad character shift for "multi-release: "
private static final byte[] MULTIRELEASE_LASTOCC;
@@ -914,17 +915,17 @@
MULTIRELEASE_LASTOCC = new byte[64];
MULTIRELEASE_LASTOCC[(int)'M' - 32] = 1;
- MULTIRELEASE_LASTOCC[(int)'U' - 32] = 2;
- MULTIRELEASE_LASTOCC[(int)'T' - 32] = 4;
MULTIRELEASE_LASTOCC[(int)'I' - 32] = 5;
MULTIRELEASE_LASTOCC[(int)'-' - 32] = 6;
- MULTIRELEASE_LASTOCC[(int)'R' - 32] = 7;
MULTIRELEASE_LASTOCC[(int)'L' - 32] = 9;
MULTIRELEASE_LASTOCC[(int)'A' - 32] = 11;
MULTIRELEASE_LASTOCC[(int)'S' - 32] = 12;
- MULTIRELEASE_LASTOCC[(int)'E' - 32] = 13;
MULTIRELEASE_LASTOCC[(int)':' - 32] = 14;
MULTIRELEASE_LASTOCC[(int)' ' - 32] = 15;
+ MULTIRELEASE_LASTOCC[(int)'T' - 32] = 16;
+ MULTIRELEASE_LASTOCC[(int)'R' - 32] = 17;
+ MULTIRELEASE_LASTOCC[(int)'U' - 32] = 18;
+ MULTIRELEASE_LASTOCC[(int)'E' - 32] = 19;
}
private JarEntry getManEntry() {
@@ -966,7 +967,7 @@
* Since there are no repeated substring in our search strings,
* the good suffix shifts can be replaced with a comparison.
*/
- private boolean match(byte[] src, byte[] b, byte[] lastOcc) {
+ private int match(byte[] src, byte[] b, byte[] lastOcc) {
int len = src.length;
int last = b.length - len;
int i = 0;
@@ -990,9 +991,9 @@
continue next;
}
}
- return true;
+ return i;
}
- return false;
+ return -1;
}
/**
@@ -1011,11 +1012,35 @@
if (manEntry != null) {
byte[] b = getBytes(manEntry);
hasClassPathAttribute = match(CLASSPATH_CHARS, b,
- CLASSPATH_LASTOCC);
+ CLASSPATH_LASTOCC) != -1;
// is this a multi-release jar file
if (MULTI_RELEASE_ENABLED && version != BASE_VERSION) {
- isMultiRelease = match(MULTIRELEASE_CHARS, b,
- MULTIRELEASE_LASTOCC);
+ int i = match(MULTIRELEASE_CHARS, b, MULTIRELEASE_LASTOCC);
+ if (i != -1) {
+ i += MULTIRELEASE_CHARS.length;
+ if (i < b.length) {
+ byte c = b[i++];
+ // Check that the value is followed by a newline
+ // and does not have a continuation
+ if (c == '\n' &&
+ (i == b.length || b[i] != ' ')) {
+ isMultiRelease = true;
+ } else if (c == '\r') {
+ if (i == b.length) {
+ isMultiRelease = true;
+ } else {
+ c = b[i++];
+ if (c == '\n') {
+ if (i == b.length || b[i] != ' ') {
+ isMultiRelease = true;
+ }
+ } else if (c != ' ') {
+ isMultiRelease = true;
+ }
+ }
+ }
+ }
+ }
}
}
hasCheckedSpecialAttributes = true;
--- a/jdk/test/java/util/jar/JarFile/MultiReleaseJarAPI.java Tue Apr 12 13:53:48 2016 +0800
+++ b/jdk/test/java/util/jar/JarFile/MultiReleaseJarAPI.java Tue Apr 12 18:25:10 2016 +0200
@@ -54,6 +54,7 @@
static final int MAJOR_VERSION = Version.current().major();
String userdir = System.getProperty("user.dir",".");
+ CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
File unversioned = new File(userdir, "unversioned.jar");
File multirelease = new File(userdir, "multi-release.jar");
File signedmultirelease = new File(userdir, "signed-multi-release.jar");
@@ -62,7 +63,6 @@
@BeforeClass
public void initialize() throws Exception {
- CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
creator.compileEntries();
creator.buildUnversionedJar();
creator.buildMultiReleaseJar();
@@ -82,6 +82,10 @@
Assert.assertFalse(jf.isMultiRelease());
}
+ try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Release.RUNTIME)) {
+ Assert.assertFalse(jf.isMultiRelease());
+ }
+
try (JarFile jf = new JarFile(multirelease)) {
Assert.assertFalse(jf.isMultiRelease());
}
@@ -89,6 +93,28 @@
try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Release.RUNTIME)) {
Assert.assertTrue(jf.isMultiRelease());
}
+
+ testCustomMultiReleaseValue("true", true);
+ testCustomMultiReleaseValue("true\r\nOther: value", true);
+ testCustomMultiReleaseValue("true\nOther: value", true);
+ testCustomMultiReleaseValue("true\rOther: value", true);
+
+ testCustomMultiReleaseValue("false", false);
+ testCustomMultiReleaseValue(" true", false);
+ testCustomMultiReleaseValue("true ", false);
+ testCustomMultiReleaseValue("true\n ", false);
+ testCustomMultiReleaseValue("true\r ", false);
+ testCustomMultiReleaseValue("true\n true", false);
+ testCustomMultiReleaseValue("true\r\n true", false);
+ }
+
+ private void testCustomMultiReleaseValue(String value, boolean expected) throws Exception {
+ creator.buildCustomMultiReleaseJar("custom-mr.jar", value);
+ File custom = new File(userdir, "custom-mr.jar");
+ try (JarFile jf = new JarFile(custom, true, ZipFile.OPEN_READ, Release.RUNTIME)) {
+ Assert.assertEquals(jf.isMultiRelease(), expected);
+ }
+ Files.delete(custom.toPath());
}
@Test
--- a/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java Tue Apr 12 13:53:48 2016 +0800
+++ b/jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java Tue Apr 12 18:25:10 2016 +0200
@@ -88,8 +88,12 @@
}
public void buildMultiReleaseJar() throws IOException {
- JarBuilder jb = new JarBuilder("multi-release.jar");
- jb.addAttribute("Multi-Release", "true");
+ buildCustomMultiReleaseJar("multi-release.jar", "true");
+ }
+
+ public void buildCustomMultiReleaseJar(String filename, String multiReleaseValue) throws IOException {
+ JarBuilder jb = new JarBuilder(filename);
+ jb.addAttribute("Multi-Release", multiReleaseValue);
jb.addEntry("README", readme8.getBytes());
jb.addEntry("version/Main.java", main.getBytes());
jb.addEntry("version/Main.class", rootClasses.get("version.Main"));