8153213: Jar manifest attribute "Multi-Release" accepts any value
authorredestad
Tue, 12 Apr 2016 18:25:10 +0200
changeset 37308 4c4141aac146
parent 37307 eb17ca049605
child 37309 8f530b9d18f4
8153213: Jar manifest attribute "Multi-Release" accepts any value Reviewed-by: psandoz, redestad Contributed-by: steve.drach@oracle.com
jdk/src/java.base/share/classes/java/util/jar/JarFile.java
jdk/test/java/util/jar/JarFile/MultiReleaseJarAPI.java
jdk/test/lib/testlibrary/java/util/jar/CreateMultiReleaseTestJars.java
--- 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"));