8194251: Deadlock between UsageTracker and System.getProperty() when using a malformed security policy
authorapetcher
Wed, 07 Feb 2018 09:06:43 -0500
changeset 48757 8cc67294ec56
parent 48756 ce608a09a666
child 48758 ba19a21d727d
8194251: Deadlock between UsageTracker and System.getProperty() when using a malformed security policy Summary: Disable localization of error messages produced during policy file parsing Reviewed-by: mchung, mullan
src/java.base/share/classes/sun/security/provider/PolicyFile.java
src/java.base/share/classes/sun/security/provider/PolicyParser.java
src/java.base/share/classes/sun/security/util/LocalizedMessage.java
test/jdk/sun/security/util/Resources/customSysClassLoader/MessageFormatting.java
test/jdk/sun/security/util/Resources/early/EarlyResources.java
test/jdk/sun/security/util/Resources/early/malformed.policy
--- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Tue Feb 06 18:28:23 2018 -0800
+++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Wed Feb 07 09:06:43 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -580,8 +580,8 @@
                 k.add(policy);
                 return k;
             });
-            Object[] source = {policy, pe.getLocalizedMessage()};
-            System.err.println(LocalizedMessage.getMessage
+            Object[] source = {policy, pe.getNonlocalizedMessage()};
+            System.err.println(LocalizedMessage.getNonlocalized
                 (POLICY + ".error.parsing.policy.message", source));
             if (debug != null) {
                 pe.printStackTrace();
@@ -808,14 +808,14 @@
                     Object[] source = {pe.permission,
                                        ite.getTargetException().toString()};
                     System.err.println(
-                        LocalizedMessage.getMessage(
+                        LocalizedMessage.getNonlocalized(
                             POLICY + ".error.adding.Permission.perm.message",
                             source));
                 } catch (Exception e) {
                     Object[] source = {pe.permission,
                                        e.toString()};
                     System.err.println(
-                        LocalizedMessage.getMessage(
+                        LocalizedMessage.getNonlocalized(
                             POLICY + ".error.adding.Permission.perm.message",
                             source));
                 }
@@ -826,7 +826,7 @@
         } catch (Exception e) {
             Object[] source = {e.toString()};
             System.err.println(
-                LocalizedMessage.getMessage(
+                LocalizedMessage.getNonlocalized(
                     POLICY + ".error.adding.Entry.message",
                     source));
         }
@@ -1803,7 +1803,7 @@
                 if (colonIndex == -1) {
                     Object[] source = {pe.name};
                     throw new Exception(
-                        LocalizedMessage.getMessage(
+                        LocalizedMessage.getNonlocalized(
                             "alias.name.not.provided.pe.name.",
                             source));
                 }
@@ -1811,7 +1811,7 @@
                 if ((suffix = getDN(suffix, keystore)) == null) {
                     Object[] source = {value.substring(colonIndex+1)};
                     throw new Exception(
-                        LocalizedMessage.getMessage(
+                        LocalizedMessage.getNonlocalized(
                             "unable.to.perform.substitution.on.alias.suffix",
                             source));
                 }
@@ -1821,7 +1821,7 @@
             } else {
                 Object[] source = {prefix};
                 throw new Exception(
-                    LocalizedMessage.getMessage(
+                    LocalizedMessage.getNonlocalized(
                         "substitution.value.prefix.unsupported",
                         source));
             }
@@ -2037,7 +2037,7 @@
             super(type);
             if (type == null) {
                 throw new NullPointerException
-                    (LocalizedMessage.getMessage("type.can.t.be.null"));
+                    (LocalizedMessage.getNonlocalized("type.can.t.be.null"));
             }
             this.type = type;
             this.name = name;
--- a/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Tue Feb 06 18:28:23 2018 -0800
+++ b/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Wed Feb 07 09:06:43 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -205,8 +205,8 @@
                     if (!domainEntries.containsKey(domainName)) {
                         domainEntries.put(domainName, de);
                     } else {
-                        LocalizedMessage localizedMsg =
-                            new LocalizedMessage("duplicate.keystore.domain.name");
+                        LocalizedMessage localizedMsg = new LocalizedMessage(
+                            "duplicate.keystore.domain.name");
                         Object[] source = {domainName};
                         String msg = "duplicate keystore domain name: " +
                                      domainName;
@@ -220,7 +220,7 @@
         }
 
         if (keyStoreUrlString == null && storePassURL != null) {
-            throw new ParsingException(LocalizedMessage.getMessage
+            throw new ParsingException(LocalizedMessage.getNonlocalized
                 ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
         }
     }
@@ -362,7 +362,7 @@
             keyStoreType = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                LocalizedMessage.getMessage("expected.keystore.type"));
+                LocalizedMessage.getNonlocalized("expected.keystore.type"));
         }
 
         // parse keystore provider
@@ -375,7 +375,7 @@
             keyStoreProvider = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                LocalizedMessage.getMessage("expected.keystore.provider"));
+                LocalizedMessage.getNonlocalized("expected.keystore.provider"));
         }
     }
 
@@ -425,7 +425,7 @@
                 if (e.codeBase != null)
                     throw new ParsingException(
                             st.lineno(),
-                            LocalizedMessage.getMessage
+                            LocalizedMessage.getNonlocalized
                                 ("multiple.Codebase.expressions"));
                 e.codeBase = match("quoted string");
                 peekAndMatch(",");
@@ -433,7 +433,7 @@
                 if (e.signedBy != null)
                     throw new ParsingException(
                             st.lineno(),
-                            LocalizedMessage.getMessage
+                            LocalizedMessage.getNonlocalized
                                 ("multiple.SignedBy.expressions"));
                 e.signedBy = match("quoted string");
 
@@ -452,7 +452,7 @@
                 if (actr <= cctr)
                     throw new ParsingException(
                             st.lineno(),
-                            LocalizedMessage.getMessage
+                            LocalizedMessage.getNonlocalized
                                 ("SignedBy.has.empty.alias"));
 
                 peekAndMatch(",");
@@ -495,7 +495,7 @@
                         }
                         throw new ParsingException
                                 (st.lineno(),
-                                LocalizedMessage.getMessage
+                                LocalizedMessage.getNonlocalized
                                     ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
                     }
                 }
@@ -532,7 +532,7 @@
 
             } else {
                 throw new ParsingException(st.lineno(),
-                    LocalizedMessage.getMessage
+                    LocalizedMessage.getNonlocalized
                         ("expected.codeBase.or.SignedBy.or.Principal"));
             }
         }
@@ -556,7 +556,7 @@
             } else {
                 throw new
                     ParsingException(st.lineno(),
-                        LocalizedMessage.getMessage
+                        LocalizedMessage.getNonlocalized
                             ("expected.permission.entry"));
             }
         }
@@ -733,7 +733,7 @@
         switch (lookahead) {
         case StreamTokenizer.TT_NUMBER:
             throw new ParsingException(st.lineno(), expect,
-                LocalizedMessage.getMessage("number.") +
+                LocalizedMessage.getNonlocalized("number.") +
                     String.valueOf(st.nval));
         case StreamTokenizer.TT_EOF:
             LocalizedMessage localizedMsg = new LocalizedMessage
@@ -826,10 +826,10 @@
             switch (lookahead) {
             case StreamTokenizer.TT_NUMBER:
                 throw new ParsingException(st.lineno(), ";",
-                        LocalizedMessage.getMessage("number.") +
+                        LocalizedMessage.getNonlocalized("number.") +
                             String.valueOf(st.nval));
             case StreamTokenizer.TT_EOF:
-                throw new ParsingException(LocalizedMessage.getMessage
+                throw new ParsingException(LocalizedMessage.getNonlocalized
                         ("expected.read.end.of.file."));
             default:
                 lookahead = st.nextToken();
@@ -987,7 +987,7 @@
          */
         public PrincipalEntry(String principalClass, String principalName) {
             if (principalClass == null || principalName == null)
-                throw new NullPointerException(LocalizedMessage.getMessage
+                throw new NullPointerException(LocalizedMessage.getNonlocalized
                     ("null.principalClass.or.principalName"));
             this.principalClass = principalClass;
             this.principalName = principalName;
@@ -1339,8 +1339,6 @@
 
         public ParsingException(int line, String msg) {
             super("line " + line + ": " + msg);
-            // don't call form.format unless getLocalizedMessage is called
-            // to avoid unnecessary permission checks
             localizedMsg = new LocalizedMessage("line.number.msg");
             source = new Object[] {line, msg};
         }
@@ -1348,16 +1346,14 @@
         public ParsingException(int line, String expect, String actual) {
             super("line " + line + ": expected [" + expect +
                 "], found [" + actual + "]");
-            // don't call form.format unless getLocalizedMessage is called
-            // to avoid unnecessary permission checks
             localizedMsg = new LocalizedMessage
                 ("line.number.expected.expect.found.actual.");
             source = new Object[] {line, expect, actual};
         }
 
-        @Override
-        public String getLocalizedMessage() {
-            return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
+        public String getNonlocalizedMessage() {
+            return i18nMessage != null ? i18nMessage :
+                localizedMsg.formatNonlocalized(source);
         }
     }
 
--- a/src/java.base/share/classes/sun/security/util/LocalizedMessage.java	Tue Feb 06 18:28:23 2018 -0800
+++ b/src/java.base/share/classes/sun/security/util/LocalizedMessage.java	Wed Feb 07 09:06:43 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -42,7 +42,7 @@
 
 public class LocalizedMessage {
 
-    private static final Resources resources = new Resources();
+    private static final Resources RESOURCES = new Resources();
 
     private final String key;
 
@@ -59,16 +59,28 @@
 
     /**
      * Return a localized string corresponding to the key stored in this
-     * object, formatted with the provided arguments. When the VM is booted,
-     * this method will obtain the correct localized message and format it
-     * using java.text.MessageFormat. Otherwise, a non-localized string is
-     * returned, and the formatting is performed by simplified formatting code.
+     * object, formatted with the provided arguments. This method should only
+     * be called when the VM is booted and all resources needed to obtain
+     * and format the localized message are loaded (or can be loaded).
      *
      * @param arguments The arguments that should be placed in the message
      * @return A formatted message string
      */
-    public String format(Object... arguments) {
-        return getMessage(key, arguments);
+    public String formatLocalized(Object... arguments) {
+        return getLocalized(key, arguments);
+    }
+
+    /**
+     * Return a non-localized string corresponding to the key stored in this
+     * object, formatted with the provided arguments. All strings are obtained
+     * from sun.security.util.Resources, and the formatting only supports
+     * simple positional argument replacement (e.g. {1}).
+     *
+     * @param arguments The arguments that should be placed in the message
+     * @return A formatted message string
+     */
+    public String formatNonlocalized(Object... arguments) {
+        return getNonlocalized(key, arguments);
     }
 
     /**
@@ -81,10 +93,10 @@
      * @param arguments The arguments that should be placed in the message
      * @return A formatted message string
      */
-    public static String getMessageUnbooted(String key,
-                                            Object... arguments) {
+    public static String getNonlocalized(String key,
+                                                Object... arguments) {
 
-        String value = resources.getString(key);
+        String value = RESOURCES.getString(key);
         if (arguments == null || arguments.length == 0) {
             return value;
         }
@@ -110,8 +122,7 @@
             try {
                 int index = Integer.parseInt(indexStr);
                 sb.append(arguments[index]);
-            }
-            catch(NumberFormatException e) {
+            } catch (NumberFormatException e) {
                 // argument index is not an integer
                 throw new RuntimeException("not an integer: " + indexStr);
             }
@@ -123,29 +134,22 @@
 
     /**
      * Return a localized string corresponding to the provided key, and
-     * formatted with the provided arguments. When the VM is booted, this
-     * method will obtain the correct localized message and format it using
-     * java.text.MessageFormat. Otherwise, a non-localized string is returned,
-     * and the formatting is performed by simplified formatting code.
+     * formatted with the provided arguments. This method should only be
+     * called when the VM is booted and all resources needed to obtain
+     * and format the localized message are loaded (or can be loaded).
      *
      * @param key The key of the desired string in the security resource bundle
      * @param arguments The arguments that should be placed in the message
      * @return A formatted message string
      */
-    public static String getMessage(String key,
-                                    Object... arguments) {
+    public static String getLocalized(String key, Object... arguments) {
 
-        if (jdk.internal.misc.VM.isBooted()) {
-            // Localization and formatting resources are available
-            String value = ResourcesMgr.getString(key);
-            if (arguments == null) {
-                return value;
-            }
-            java.text.MessageFormat form = new java.text.MessageFormat(value);
-            return form.format(arguments);
-        } else {
-            return getMessageUnbooted(key, arguments);
+        String value = ResourcesMgr.getString(key);
+        if (arguments == null) {
+            return value;
         }
+        java.text.MessageFormat form = new java.text.MessageFormat(value);
+        return form.format(arguments);
     }
 
 }
--- a/test/jdk/sun/security/util/Resources/customSysClassLoader/MessageFormatting.java	Tue Feb 06 18:28:23 2018 -0800
+++ b/test/jdk/sun/security/util/Resources/customSysClassLoader/MessageFormatting.java	Wed Feb 07 09:06:43 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,7 +44,8 @@
         Enumeration<String> keys = resources.getKeys();
         while (keys.hasMoreElements()) {
             String curKey = keys.nextElement();
-            String formattedString = LocalizedMessage.getMessageUnbooted(curKey, MSG_ARGS);
+            String formattedString =
+                LocalizedMessage.getNonlocalized(curKey, MSG_ARGS);
             String msg = resources.getString(curKey);
             String expectedString = formatIfNecessary(msg, MSG_ARGS);
             if (!formattedString.equals(expectedString)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/Resources/early/EarlyResources.java	Wed Feb 07 09:06:43 2018 -0500
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 8194251
+ * @summary Ensure that messages can be formatted before resources are loaded
+ * @library /test/lib
+ * @build jdk.test.lib.process.*
+ * @run main EarlyResources
+ */
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+import jdk.test.lib.process.*;
+
+public class EarlyResources {
+
+    public static void main(String[] args) throws Exception {
+
+        String testSrc = System.getProperty("test.src");
+        String fs = File.separator;
+        String policyPath = testSrc + fs + "malformed.policy";
+
+        OutputAnalyzer out = ProcessTools.executeTestJvm(
+            "-Djava.security.manager",
+            "-Djava.security.policy=" + policyPath,
+            "EarlyResources$TestMain");
+
+        out.shouldHaveExitValue(0);
+    }
+
+    public static class TestMain {
+        public static void main(String[] args) {
+            System.out.println(new Date().toString());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/util/Resources/early/malformed.policy	Wed Feb 07 09:06:43 2018 -0500
@@ -0,0 +1,3 @@
+grant {
+    xyz;
+}