8194251: Deadlock between UsageTracker and System.getProperty() when using a malformed security policy

Disable localization of error messages produced during policy file parsing

Reviewed-by: mchung, mullan
This commit is contained in:
Adam Petcher 2018-02-07 09:06:43 -05:00
parent 999168d66b
commit 683817de43
6 changed files with 126 additions and 64 deletions

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -580,8 +580,8 @@ public class PolicyFile extends java.security.Policy {
k.add(policy); k.add(policy);
return k; return k;
}); });
Object[] source = {policy, pe.getLocalizedMessage()}; Object[] source = {policy, pe.getNonlocalizedMessage()};
System.err.println(LocalizedMessage.getMessage System.err.println(LocalizedMessage.getNonlocalized
(POLICY + ".error.parsing.policy.message", source)); (POLICY + ".error.parsing.policy.message", source));
if (debug != null) { if (debug != null) {
pe.printStackTrace(); pe.printStackTrace();
@ -808,14 +808,14 @@ public class PolicyFile extends java.security.Policy {
Object[] source = {pe.permission, Object[] source = {pe.permission,
ite.getTargetException().toString()}; ite.getTargetException().toString()};
System.err.println( System.err.println(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Permission.perm.message", POLICY + ".error.adding.Permission.perm.message",
source)); source));
} catch (Exception e) { } catch (Exception e) {
Object[] source = {pe.permission, Object[] source = {pe.permission,
e.toString()}; e.toString()};
System.err.println( System.err.println(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Permission.perm.message", POLICY + ".error.adding.Permission.perm.message",
source)); source));
} }
@ -826,7 +826,7 @@ public class PolicyFile extends java.security.Policy {
} catch (Exception e) { } catch (Exception e) {
Object[] source = {e.toString()}; Object[] source = {e.toString()};
System.err.println( System.err.println(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
POLICY + ".error.adding.Entry.message", POLICY + ".error.adding.Entry.message",
source)); source));
} }
@ -1803,7 +1803,7 @@ public class PolicyFile extends java.security.Policy {
if (colonIndex == -1) { if (colonIndex == -1) {
Object[] source = {pe.name}; Object[] source = {pe.name};
throw new Exception( throw new Exception(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
"alias.name.not.provided.pe.name.", "alias.name.not.provided.pe.name.",
source)); source));
} }
@ -1811,7 +1811,7 @@ public class PolicyFile extends java.security.Policy {
if ((suffix = getDN(suffix, keystore)) == null) { if ((suffix = getDN(suffix, keystore)) == null) {
Object[] source = {value.substring(colonIndex+1)}; Object[] source = {value.substring(colonIndex+1)};
throw new Exception( throw new Exception(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
"unable.to.perform.substitution.on.alias.suffix", "unable.to.perform.substitution.on.alias.suffix",
source)); source));
} }
@ -1821,7 +1821,7 @@ public class PolicyFile extends java.security.Policy {
} else { } else {
Object[] source = {prefix}; Object[] source = {prefix};
throw new Exception( throw new Exception(
LocalizedMessage.getMessage( LocalizedMessage.getNonlocalized(
"substitution.value.prefix.unsupported", "substitution.value.prefix.unsupported",
source)); source));
} }
@ -2037,7 +2037,7 @@ public class PolicyFile extends java.security.Policy {
super(type); super(type);
if (type == null) { if (type == null) {
throw new NullPointerException throw new NullPointerException
(LocalizedMessage.getMessage("type.can.t.be.null")); (LocalizedMessage.getNonlocalized("type.can.t.be.null"));
} }
this.type = type; this.type = type;
this.name = name; this.name = name;

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -205,8 +205,8 @@ public class PolicyParser {
if (!domainEntries.containsKey(domainName)) { if (!domainEntries.containsKey(domainName)) {
domainEntries.put(domainName, de); domainEntries.put(domainName, de);
} else { } else {
LocalizedMessage localizedMsg = LocalizedMessage localizedMsg = new LocalizedMessage(
new LocalizedMessage("duplicate.keystore.domain.name"); "duplicate.keystore.domain.name");
Object[] source = {domainName}; Object[] source = {domainName};
String msg = "duplicate keystore domain name: " + String msg = "duplicate keystore domain name: " +
domainName; domainName;
@ -220,7 +220,7 @@ public class PolicyParser {
} }
if (keyStoreUrlString == null && storePassURL != null) { if (keyStoreUrlString == null && storePassURL != null) {
throw new ParsingException(LocalizedMessage.getMessage throw new ParsingException(LocalizedMessage.getNonlocalized
("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore")); ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore"));
} }
} }
@ -362,7 +362,7 @@ public class PolicyParser {
keyStoreType = match("quoted string"); keyStoreType = match("quoted string");
} else { } else {
throw new ParsingException(st.lineno(), throw new ParsingException(st.lineno(),
LocalizedMessage.getMessage("expected.keystore.type")); LocalizedMessage.getNonlocalized("expected.keystore.type"));
} }
// parse keystore provider // parse keystore provider
@ -375,7 +375,7 @@ public class PolicyParser {
keyStoreProvider = match("quoted string"); keyStoreProvider = match("quoted string");
} else { } else {
throw new ParsingException(st.lineno(), throw new ParsingException(st.lineno(),
LocalizedMessage.getMessage("expected.keystore.provider")); LocalizedMessage.getNonlocalized("expected.keystore.provider"));
} }
} }
@ -425,7 +425,7 @@ public class PolicyParser {
if (e.codeBase != null) if (e.codeBase != null)
throw new ParsingException( throw new ParsingException(
st.lineno(), st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("multiple.Codebase.expressions")); ("multiple.Codebase.expressions"));
e.codeBase = match("quoted string"); e.codeBase = match("quoted string");
peekAndMatch(","); peekAndMatch(",");
@ -433,7 +433,7 @@ public class PolicyParser {
if (e.signedBy != null) if (e.signedBy != null)
throw new ParsingException( throw new ParsingException(
st.lineno(), st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("multiple.SignedBy.expressions")); ("multiple.SignedBy.expressions"));
e.signedBy = match("quoted string"); e.signedBy = match("quoted string");
@ -452,7 +452,7 @@ public class PolicyParser {
if (actr <= cctr) if (actr <= cctr)
throw new ParsingException( throw new ParsingException(
st.lineno(), st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("SignedBy.has.empty.alias")); ("SignedBy.has.empty.alias"));
peekAndMatch(","); peekAndMatch(",");
@ -495,7 +495,7 @@ public class PolicyParser {
} }
throw new ParsingException throw new ParsingException
(st.lineno(), (st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name")); ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name"));
} }
} }
@ -532,7 +532,7 @@ public class PolicyParser {
} else { } else {
throw new ParsingException(st.lineno(), throw new ParsingException(st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("expected.codeBase.or.SignedBy.or.Principal")); ("expected.codeBase.or.SignedBy.or.Principal"));
} }
} }
@ -556,7 +556,7 @@ public class PolicyParser {
} else { } else {
throw new throw new
ParsingException(st.lineno(), ParsingException(st.lineno(),
LocalizedMessage.getMessage LocalizedMessage.getNonlocalized
("expected.permission.entry")); ("expected.permission.entry"));
} }
} }
@ -733,7 +733,7 @@ public class PolicyParser {
switch (lookahead) { switch (lookahead) {
case StreamTokenizer.TT_NUMBER: case StreamTokenizer.TT_NUMBER:
throw new ParsingException(st.lineno(), expect, throw new ParsingException(st.lineno(), expect,
LocalizedMessage.getMessage("number.") + LocalizedMessage.getNonlocalized("number.") +
String.valueOf(st.nval)); String.valueOf(st.nval));
case StreamTokenizer.TT_EOF: case StreamTokenizer.TT_EOF:
LocalizedMessage localizedMsg = new LocalizedMessage LocalizedMessage localizedMsg = new LocalizedMessage
@ -826,10 +826,10 @@ public class PolicyParser {
switch (lookahead) { switch (lookahead) {
case StreamTokenizer.TT_NUMBER: case StreamTokenizer.TT_NUMBER:
throw new ParsingException(st.lineno(), ";", throw new ParsingException(st.lineno(), ";",
LocalizedMessage.getMessage("number.") + LocalizedMessage.getNonlocalized("number.") +
String.valueOf(st.nval)); String.valueOf(st.nval));
case StreamTokenizer.TT_EOF: case StreamTokenizer.TT_EOF:
throw new ParsingException(LocalizedMessage.getMessage throw new ParsingException(LocalizedMessage.getNonlocalized
("expected.read.end.of.file.")); ("expected.read.end.of.file."));
default: default:
lookahead = st.nextToken(); lookahead = st.nextToken();
@ -987,7 +987,7 @@ public class PolicyParser {
*/ */
public PrincipalEntry(String principalClass, String principalName) { public PrincipalEntry(String principalClass, String principalName) {
if (principalClass == null || principalName == null) if (principalClass == null || principalName == null)
throw new NullPointerException(LocalizedMessage.getMessage throw new NullPointerException(LocalizedMessage.getNonlocalized
("null.principalClass.or.principalName")); ("null.principalClass.or.principalName"));
this.principalClass = principalClass; this.principalClass = principalClass;
this.principalName = principalName; this.principalName = principalName;
@ -1339,8 +1339,6 @@ public class PolicyParser {
public ParsingException(int line, String msg) { public ParsingException(int line, String msg) {
super("line " + line + ": " + 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"); localizedMsg = new LocalizedMessage("line.number.msg");
source = new Object[] {line, msg}; source = new Object[] {line, msg};
} }
@ -1348,16 +1346,14 @@ public class PolicyParser {
public ParsingException(int line, String expect, String actual) { public ParsingException(int line, String expect, String actual) {
super("line " + line + ": expected [" + expect + super("line " + line + ": expected [" + expect +
"], found [" + actual + "]"); "], found [" + actual + "]");
// don't call form.format unless getLocalizedMessage is called
// to avoid unnecessary permission checks
localizedMsg = new LocalizedMessage localizedMsg = new LocalizedMessage
("line.number.expected.expect.found.actual."); ("line.number.expected.expect.found.actual.");
source = new Object[] {line, expect, actual}; source = new Object[] {line, expect, actual};
} }
@Override public String getNonlocalizedMessage() {
public String getLocalizedMessage() { return i18nMessage != null ? i18nMessage :
return i18nMessage != null ? i18nMessage : localizedMsg.format(source); localizedMsg.formatNonlocalized(source);
} }
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -42,7 +42,7 @@ package sun.security.util;
public class LocalizedMessage { public class LocalizedMessage {
private static final Resources resources = new Resources(); private static final Resources RESOURCES = new Resources();
private final String key; private final String key;
@ -59,16 +59,28 @@ public class LocalizedMessage {
/** /**
* Return a localized string corresponding to the key stored in this * Return a localized string corresponding to the key stored in this
* object, formatted with the provided arguments. When the VM is booted, * object, formatted with the provided arguments. This method should only
* this method will obtain the correct localized message and format it * be called when the VM is booted and all resources needed to obtain
* using java.text.MessageFormat. Otherwise, a non-localized string is * and format the localized message are loaded (or can be loaded).
* returned, and the formatting is performed by simplified formatting code.
* *
* @param arguments The arguments that should be placed in the message * @param arguments The arguments that should be placed in the message
* @return A formatted message string * @return A formatted message string
*/ */
public String format(Object... arguments) { public String formatLocalized(Object... arguments) {
return getMessage(key, 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 @@ public class LocalizedMessage {
* @param arguments The arguments that should be placed in the message * @param arguments The arguments that should be placed in the message
* @return A formatted message string * @return A formatted message string
*/ */
public static String getMessageUnbooted(String key, public static String getNonlocalized(String key,
Object... arguments) { Object... arguments) {
String value = resources.getString(key); String value = RESOURCES.getString(key);
if (arguments == null || arguments.length == 0) { if (arguments == null || arguments.length == 0) {
return value; return value;
} }
@ -110,8 +122,7 @@ public class LocalizedMessage {
try { try {
int index = Integer.parseInt(indexStr); int index = Integer.parseInt(indexStr);
sb.append(arguments[index]); sb.append(arguments[index]);
} } catch (NumberFormatException e) {
catch(NumberFormatException e) {
// argument index is not an integer // argument index is not an integer
throw new RuntimeException("not an integer: " + indexStr); throw new RuntimeException("not an integer: " + indexStr);
} }
@ -123,29 +134,22 @@ public class LocalizedMessage {
/** /**
* Return a localized string corresponding to the provided key, and * Return a localized string corresponding to the provided key, and
* formatted with the provided arguments. When the VM is booted, this * formatted with the provided arguments. This method should only be
* method will obtain the correct localized message and format it using * called when the VM is booted and all resources needed to obtain
* java.text.MessageFormat. Otherwise, a non-localized string is returned, * and format the localized message are loaded (or can be loaded).
* and the formatting is performed by simplified formatting code.
* *
* @param key The key of the desired string in the security resource bundle * @param key The key of the desired string in the security resource bundle
* @param arguments The arguments that should be placed in the message * @param arguments The arguments that should be placed in the message
* @return A formatted message string * @return A formatted message string
*/ */
public static String getMessage(String key, public static String getLocalized(String key, Object... arguments) {
Object... arguments) {
if (jdk.internal.misc.VM.isBooted()) { String value = ResourcesMgr.getString(key);
// Localization and formatting resources are available if (arguments == null) {
String value = ResourcesMgr.getString(key); return value;
if (arguments == null) {
return value;
}
java.text.MessageFormat form = new java.text.MessageFormat(value);
return form.format(arguments);
} else {
return getMessageUnbooted(key, arguments);
} }
java.text.MessageFormat form = new java.text.MessageFormat(value);
return form.format(arguments);
} }
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -44,7 +44,8 @@ public class MessageFormatting {
Enumeration<String> keys = resources.getKeys(); Enumeration<String> keys = resources.getKeys();
while (keys.hasMoreElements()) { while (keys.hasMoreElements()) {
String curKey = keys.nextElement(); String curKey = keys.nextElement();
String formattedString = LocalizedMessage.getMessageUnbooted(curKey, MSG_ARGS); String formattedString =
LocalizedMessage.getNonlocalized(curKey, MSG_ARGS);
String msg = resources.getString(curKey); String msg = resources.getString(curKey);
String expectedString = formatIfNecessary(msg, MSG_ARGS); String expectedString = formatIfNecessary(msg, MSG_ARGS);
if (!formattedString.equals(expectedString)) { if (!formattedString.equals(expectedString)) {

View file

@ -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());
}
}
}

View file

@ -0,0 +1,3 @@
grant {
xyz;
}