8168075: Custom system class loader + security manager + malformed policy file = recursive initialization
authorapetcher
Tue, 24 Jan 2017 16:19:21 -0500
changeset 43297 05ad35b943d0
parent 43296 0f1ac3527db2
child 43298 1793bf7f30fe
8168075: Custom system class loader + security manager + malformed policy file = recursive initialization Reviewed-by: mchung, mullan
jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java
jdk/src/java.base/share/classes/sun/security/provider/PolicyParser.java
jdk/src/java.base/share/classes/sun/security/util/LocalizedMessage.java
jdk/test/sun/security/util/Resources/customSysClassLoader/BootMessages.java
jdk/test/sun/security/util/Resources/customSysClassLoader/CustomClassLoader.java
jdk/test/sun/security/util/Resources/customSysClassLoader/MessageFormatting.java
jdk/test/sun/security/util/Resources/customSysClassLoader/error.policy
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Tue Jan 24 00:48:51 2017 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyFile.java	Tue Jan 24 16:19:21 2017 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -32,7 +32,6 @@
 import java.net.URI;
 import java.nio.file.Paths;
 import java.util.*;
-import java.text.MessageFormat;
 import java.security.*;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
@@ -579,10 +578,9 @@
                 k.add(policy);
                 return k;
             });
-            MessageFormat form = new MessageFormat(ResourcesMgr.getString
-                (POLICY + ".error.parsing.policy.message"));
             Object[] source = {policy, pe.getLocalizedMessage()};
-            System.err.println(form.format(source));
+            System.err.println(LocalizedMessage.getMessage
+                (POLICY + ".error.parsing.policy.message", source));
             if (debug != null) {
                 pe.printStackTrace();
             }
@@ -805,32 +803,30 @@
                         }
                     }
                 } catch (java.lang.reflect.InvocationTargetException ite) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                         (POLICY +
-                          ".error.adding.Permission.perm.message"));
                     Object[] source = {pe.permission,
                                        ite.getTargetException().toString()};
-                    System.err.println(form.format(source));
+                    System.err.println(
+                        LocalizedMessage.getMessage(
+                            POLICY + ".error.adding.Permission.perm.message",
+                            source));
                 } catch (Exception e) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                         (POLICY +
-                          ".error.adding.Permission.perm.message"));
                     Object[] source = {pe.permission,
                                        e.toString()};
-                    System.err.println(form.format(source));
+                    System.err.println(
+                        LocalizedMessage.getMessage(
+                            POLICY + ".error.adding.Permission.perm.message",
+                            source));
                 }
             }
 
             // No need to sync because noone has access to newInfo yet
             newInfo.policyEntries.add(entry);
         } catch (Exception e) {
-            MessageFormat form = new MessageFormat(ResourcesMgr.getString
-                                         (POLICY
-                                         + ".error.adding.Entry.message"));
             Object[] source = {e.toString()};
-            System.err.println(form.format(source));
+            System.err.println(
+                LocalizedMessage.getMessage(
+                    POLICY + ".error.adding.Entry.message",
+                    source));
         }
         if (debug != null)
             debug.println();
@@ -1803,29 +1799,29 @@
             } else if (prefix.equalsIgnoreCase("alias")) {
                 // get the suffix and perform keystore alias replacement
                 if (colonIndex == -1) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("alias.name.not.provided.pe.name."));
                     Object[] source = {pe.name};
-                    throw new Exception(form.format(source));
+                    throw new Exception(
+                        LocalizedMessage.getMessage(
+                            "alias.name.not.provided.pe.name.",
+                            source));
                 }
                 suffix = value.substring(colonIndex+1);
                 if ((suffix = getDN(suffix, keystore)) == null) {
-                    MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("unable.to.perform.substitution.on.alias.suffix"));
                     Object[] source = {value.substring(colonIndex+1)};
-                    throw new Exception(form.format(source));
+                    throw new Exception(
+                        LocalizedMessage.getMessage(
+                            "unable.to.perform.substitution.on.alias.suffix",
+                            source));
                 }
 
                 sb.append(X500PRINCIPAL + " \"" + suffix + "\"");
                 startIndex = e+2;
             } else {
-                MessageFormat form = new MessageFormat
-                        (ResourcesMgr.getString
-                        ("substitution.value.prefix.unsupported"));
                 Object[] source = {prefix};
-                throw new Exception(form.format(source));
+                throw new Exception(
+                    LocalizedMessage.getMessage(
+                        "substitution.value.prefix.unsupported",
+                        source));
             }
         }
 
@@ -2039,7 +2035,7 @@
             super(type);
             if (type == null) {
                 throw new NullPointerException
-                    (ResourcesMgr.getString("type.can.t.be.null"));
+                    (LocalizedMessage.getMessage("type.can.t.be.null"));
             }
             this.type = type;
             this.name = name;
--- a/jdk/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Tue Jan 24 00:48:51 2017 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/provider/PolicyParser.java	Tue Jan 24 16:19:21 2017 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -26,18 +26,14 @@
 package sun.security.provider;
 
 import java.io.*;
-import java.lang.RuntimePermission;
-import java.net.SocketPermission;
-import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.security.Principal;
-import java.text.MessageFormat;
 import java.util.*;
 import javax.security.auth.x500.X500Principal;
 
 import sun.security.util.Debug;
 import sun.security.util.PropertyExpander;
-import sun.security.util.ResourcesMgr;
+import sun.security.util.LocalizedMessage;
 
 /**
  * The policy for a Java runtime (specifying
@@ -209,13 +205,12 @@
                     if (!domainEntries.containsKey(domainName)) {
                         domainEntries.put(domainName, de);
                     } else {
-                        MessageFormat form =
-                            new MessageFormat(ResourcesMgr.getString(
-                                "duplicate.keystore.domain.name"));
+                        LocalizedMessage localizedMsg =
+                            new LocalizedMessage("duplicate.keystore.domain.name");
                         Object[] source = {domainName};
                         String msg = "duplicate keystore domain name: " +
                                      domainName;
-                        throw new ParsingException(msg, form, source);
+                        throw new ParsingException(msg, localizedMsg, source);
                     }
                 }
             } else {
@@ -225,7 +220,7 @@
         }
 
         if (keyStoreUrlString == null && storePassURL != null) {
-            throw new ParsingException(ResourcesMgr.getString
+            throw new ParsingException(LocalizedMessage.getMessage
                 ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
         }
     }
@@ -367,7 +362,7 @@
             keyStoreType = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                        ResourcesMgr.getString("expected.keystore.type"));
+                LocalizedMessage.getMessage("expected.keystore.type"));
         }
 
         // parse keystore provider
@@ -380,7 +375,7 @@
             keyStoreProvider = match("quoted string");
         } else {
             throw new ParsingException(st.lineno(),
-                        ResourcesMgr.getString("expected.keystore.provider"));
+                LocalizedMessage.getMessage("expected.keystore.provider"));
         }
     }
 
@@ -430,7 +425,7 @@
                 if (e.codeBase != null)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString
+                            LocalizedMessage.getMessage
                                 ("multiple.Codebase.expressions"));
                 e.codeBase = match("quoted string");
                 peekAndMatch(",");
@@ -438,8 +433,8 @@
                 if (e.signedBy != null)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString(
-                                "multiple.SignedBy.expressions"));
+                            LocalizedMessage.getMessage
+                                ("multiple.SignedBy.expressions"));
                 e.signedBy = match("quoted string");
 
                 // verify syntax of the aliases
@@ -457,8 +452,8 @@
                 if (actr <= cctr)
                     throw new ParsingException(
                             st.lineno(),
-                            ResourcesMgr.getString(
-                                "SignedBy.has.empty.alias"));
+                            LocalizedMessage.getMessage
+                                ("SignedBy.has.empty.alias"));
 
                 peekAndMatch(",");
             } else if (peekAndMatch("Principal")) {
@@ -500,7 +495,7 @@
                         }
                         throw new ParsingException
                                 (st.lineno(),
-                                 ResourcesMgr.getString
+                                LocalizedMessage.getMessage
                                     ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
                     }
                 }
@@ -537,8 +532,8 @@
 
             } else {
                 throw new ParsingException(st.lineno(),
-                                  ResourcesMgr.getString(
-                                      "expected.codeBase.or.SignedBy.or.Principal"));
+                    LocalizedMessage.getMessage
+                        ("expected.codeBase.or.SignedBy.or.Principal"));
             }
         }
 
@@ -561,8 +556,8 @@
             } else {
                 throw new
                     ParsingException(st.lineno(),
-                                     ResourcesMgr.getString(
-                                        "expected.permission.entry"));
+                        LocalizedMessage.getMessage
+                            ("expected.permission.entry"));
             }
         }
         match("}");
@@ -738,15 +733,14 @@
         switch (lookahead) {
         case StreamTokenizer.TT_NUMBER:
             throw new ParsingException(st.lineno(), expect,
-                                       ResourcesMgr.getString("number.") +
-                                       String.valueOf(st.nval));
+                LocalizedMessage.getMessage("number.") +
+                    String.valueOf(st.nval));
         case StreamTokenizer.TT_EOF:
-            MessageFormat form = new MessageFormat(
-                    ResourcesMgr.getString
-                            ("expected.expect.read.end.of.file."));
+            LocalizedMessage localizedMsg = new LocalizedMessage
+                ("expected.expect.read.end.of.file.");
             Object[] source = {expect};
             String msg = "expected [" + expect + "], read [end of file]";
-            throw new ParsingException(msg, form, source);
+            throw new ParsingException(msg, localizedMsg, source);
         case StreamTokenizer.TT_WORD:
             if (expect.equalsIgnoreCase(st.sval)) {
                 lookahead = st.nextToken();
@@ -832,10 +826,10 @@
             switch (lookahead) {
             case StreamTokenizer.TT_NUMBER:
                 throw new ParsingException(st.lineno(), ";",
-                                          ResourcesMgr.getString("number.") +
-                                          String.valueOf(st.nval));
+                        LocalizedMessage.getMessage("number.") +
+                            String.valueOf(st.nval));
             case StreamTokenizer.TT_EOF:
-                throw new ParsingException(ResourcesMgr.getString
+                throw new ParsingException(LocalizedMessage.getMessage
                         ("expected.read.end.of.file."));
             default:
                 lookahead = st.nextToken();
@@ -993,8 +987,8 @@
          */
         public PrincipalEntry(String principalClass, String principalName) {
             if (principalClass == null || principalName == null)
-                throw new NullPointerException(ResourcesMgr.getString(
-                                  "null.principalClass.or.principalName"));
+                throw new NullPointerException(LocalizedMessage.getMessage
+                    ("null.principalClass.or.principalName"));
             this.principalClass = principalClass;
             this.principalName = principalName;
         }
@@ -1244,11 +1238,11 @@
             if (!entries.containsKey(keystoreName)) {
                 entries.put(keystoreName, entry);
             } else {
-                MessageFormat form = new MessageFormat(ResourcesMgr.getString(
-                    "duplicate.keystore.name"));
+                LocalizedMessage localizedMsg = new LocalizedMessage
+                    ("duplicate.keystore.name");
                 Object[] source = {keystoreName};
                 String msg = "duplicate keystore name: " + keystoreName;
-                throw new ParsingException(msg, form, source);
+                throw new ParsingException(msg, localizedMsg, source);
             }
         }
 
@@ -1320,7 +1314,7 @@
         private static final long serialVersionUID = -4330692689482574072L;
 
         private String i18nMessage;
-        private MessageFormat form;
+        private LocalizedMessage localizedMsg;
         private Object[] source;
 
         /**
@@ -1336,10 +1330,10 @@
             i18nMessage = msg;
         }
 
-        public ParsingException(String msg, MessageFormat form,
+        public ParsingException(String msg, LocalizedMessage localizedMsg,
                                 Object[] source) {
             super(msg);
-            this.form = form;
+            this.localizedMsg = localizedMsg;
             this.source = source;
         }
 
@@ -1347,7 +1341,7 @@
             super("line " + line + ": " + msg);
             // don't call form.format unless getLocalizedMessage is called
             // to avoid unnecessary permission checks
-            form = new MessageFormat(ResourcesMgr.getString("line.number.msg"));
+            localizedMsg = new LocalizedMessage("line.number.msg");
             source = new Object[] {line, msg};
         }
 
@@ -1356,14 +1350,14 @@
                 "], found [" + actual + "]");
             // don't call form.format unless getLocalizedMessage is called
             // to avoid unnecessary permission checks
-            form = new MessageFormat(ResourcesMgr.getString
-                ("line.number.expected.expect.found.actual."));
+            localizedMsg = new LocalizedMessage
+                ("line.number.expected.expect.found.actual.");
             source = new Object[] {line, expect, actual};
         }
 
         @Override
         public String getLocalizedMessage() {
-            return i18nMessage != null ? i18nMessage : form.format(source);
+            return i18nMessage != null ? i18nMessage : localizedMsg.format(source);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/LocalizedMessage.java	Tue Jan 24 16:19:21 2017 -0500
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+package sun.security.util;
+
+/**
+ * This class produces formatted and localized messages describing security
+ * issues. Some messages may be required when the VM is not fully booted. In
+ * this case, localization resources and classes used for message formatting
+ * may not be available. When the VM is not booted, the message will not be
+ * localized, and it will be formatted using simplified message formatting
+ * code that is contained in this class.
+ */
+
+/*
+ * Some of this code is executed before the VM is fully booted. Some import
+ * statements have been omitted to help prevent accidental use of classes that
+ * may not be available during boot.
+ */
+
+public class LocalizedMessage {
+
+    private static final Resources resources = new Resources();
+
+    private final String key;
+
+    /**
+     * A LocalizedMessage can be instantiated with a key and formatted with
+     * arguments later in the style of MessageFormat. This organization
+     * allows the actual formatting (and associated permission checks) to be
+     * avoided unless the resulting string is needed.
+     * @param key
+     */
+    public LocalizedMessage(String key) {
+        this.key = key;
+    }
+
+    /**
+     * 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.
+     *
+     * @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);
+    }
+
+    /**
+     * Return a non-localized string corresponding to the provided key, and
+     * 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 key The key of the desired string in Resources
+     * @param arguments The arguments that should be placed in the message
+     * @return A formatted message string
+     */
+    public static String getMessageUnbooted(String key,
+                                            Object... arguments) {
+
+        String value = resources.getString(key);
+        if (arguments == null || arguments.length == 0) {
+            return value;
+        }
+        // Classes like StringTokenizer may not be loaded, so parsing
+        //   is performed with String methods
+        StringBuilder sb = new StringBuilder();
+        int nextBraceIndex;
+        while ((nextBraceIndex = value.indexOf('{')) >= 0) {
+
+            String firstPart = value.substring(0, nextBraceIndex);
+            sb.append(firstPart);
+            value = value.substring(nextBraceIndex + 1);
+
+            // look for closing brace and argument index
+            nextBraceIndex = value.indexOf('}');
+            if (nextBraceIndex < 0) {
+                // no closing brace
+                // MessageFormat would throw IllegalArgumentException, but
+                //   that exception class may not be loaded yet
+                throw new RuntimeException("Unmatched braces");
+            }
+            String indexStr = value.substring(0, nextBraceIndex);
+            try {
+                int index = Integer.parseInt(indexStr);
+                sb.append(arguments[index]);
+            }
+            catch(NumberFormatException e) {
+                // argument index is not an integer
+                throw new RuntimeException("not an integer: " + indexStr);
+            }
+            value = value.substring(nextBraceIndex + 1);
+        }
+        sb.append(value);
+        return sb.toString();
+    }
+
+    /**
+     * 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.
+     *
+     * @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) {
+
+        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);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/Resources/customSysClassLoader/BootMessages.java	Tue Jan 24 16:19:21 2017 -0500
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, 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 8168075
+ * @summary Ensure that security messages can be formatted during system class
+ *   loader initialization.
+ * @build CustomClassLoader
+ * @run main/othervm/java.security.policy=error.policy -Djava.security.manager -Djava.system.class.loader=CustomClassLoader BootMessages
+ */
+
+public class BootMessages {
+
+    public static void main(String[] args) throws Exception {
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/Resources/customSysClassLoader/CustomClassLoader.java	Tue Jan 24 16:19:21 2017 -0500
@@ -0,0 +1,36 @@
+
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * A class loader that can be used in tests that require a custom class
+ * loader. Its behavior is identical to ClassLoader.
+ */
+
+public class CustomClassLoader extends ClassLoader {
+    public CustomClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/Resources/customSysClassLoader/MessageFormatting.java	Tue Jan 24 16:19:21 2017 -0500
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.util.*;
+import sun.security.util.Resources;
+import sun.security.util.LocalizedMessage;
+import java.text.MessageFormat;
+
+/*
+ * @test
+ * @bug 8168075
+ * @summary Ensure that security message formatting code is capable of
+ *     displaying all messages.
+ * @modules java.base/sun.security.util
+ */
+
+public class MessageFormatting {
+
+    private static final Object[] MSG_ARGS = new Integer[]{0, 1, 2};
+
+    public static void main(String[] args) throws Exception {
+
+        Resources resources = new Resources();
+        Enumeration<String> keys = resources.getKeys();
+        while (keys.hasMoreElements()) {
+            String curKey = keys.nextElement();
+            String formattedString = LocalizedMessage.getMessageUnbooted(curKey, MSG_ARGS);
+            String msg = resources.getString(curKey);
+            String expectedString = formatIfNecessary(msg, MSG_ARGS);
+            if (!formattedString.equals(expectedString)) {
+                System.err.println("Expected string:");
+                System.err.println(expectedString);
+                System.err.println("Actual string:");
+                System.err.println(formattedString);
+                throw new Exception("Incorrect message string");
+            }
+        }
+    }
+
+    private static String formatIfNecessary(String str, Object[] args) {
+        // message formatting code only formats messages with arguments
+        if (str.indexOf('{') < 0) {
+            return str;
+        }
+        MessageFormat format = new MessageFormat(str);
+        return format.format(args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/Resources/customSysClassLoader/error.policy	Tue Jan 24 16:19:21 2017 -0500
@@ -0,0 +1,5 @@
+grant { 
+    permission java.lang.RuntimePermission "createClassLoader";
+    permission java.lang.RuntimePermission "";
+};
+