# HG changeset patch # User dfuchs # Date 1434570432 14400 # Node ID 316a8c3e572a71b96d087fbe6e2bcde54d9e6520 # Parent c8ceef0823915a279912c9c14d6ea30f5b9544fe 8072692: Improve performance of SecurityManager.checkPackageAccess Reviewed-by: mullan, weijun diff -r c8ceef082391 -r 316a8c3e572a jdk/src/java.base/share/classes/java/lang/SecurityManager.java --- a/jdk/src/java.base/share/classes/java/lang/SecurityManager.java Tue Jun 16 17:05:08 2015 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/SecurityManager.java Wed Jun 17 15:47:12 2015 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2015, 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 @@ -1447,7 +1447,7 @@ throw new NullPointerException("package name can't be null"); } - String[] pkgs; + String[] restrictedPkgs; synchronized (packageAccessLock) { /* * Do we need to update our property array? @@ -1457,8 +1457,7 @@ AccessController.doPrivileged( new PrivilegedAction<>() { public String run() { - return java.security.Security.getProperty( - "package.access"); + return Security.getProperty("package.access"); } } ); @@ -1468,14 +1467,33 @@ // Using a snapshot of packageAccess -- don't care if static field // changes afterwards; array contents won't change. - pkgs = packageAccess; + restrictedPkgs = packageAccess; } /* * Traverse the list of packages, check for any matches. */ - for (String restrictedPkg : pkgs) { - if (pkg.startsWith(restrictedPkg) || restrictedPkg.equals(pkg + ".")) { + final int plen = pkg.length(); + for (String restrictedPkg : restrictedPkgs) { + final int rlast = restrictedPkg.length() - 1; + + // Optimizations: + // + // If rlast >= plen then restrictedPkg is longer than pkg by at + // least one char. This means pkg cannot start with restrictedPkg, + // since restrictedPkg will be longer than pkg. + // + // Similarly if rlast != plen, then pkg + "." cannot be the same + // as restrictedPkg, since pkg + "." will have a different length + // than restrictedPkg. + // + if (rlast < plen && pkg.startsWith(restrictedPkg) || + // The following test is equivalent to + // restrictedPkg.equals(pkg + ".") but is noticeably more + // efficient: + rlast == plen && restrictedPkg.startsWith(pkg) && + restrictedPkg.charAt(rlast) == '.') + { checkPermission( new RuntimePermission("accessClassInPackage." + pkg)); break; // No need to continue; only need to check this once diff -r c8ceef082391 -r 316a8c3e572a jdk/test/java/lang/SecurityManager/CheckPackageAccess.java --- a/jdk/test/java/lang/SecurityManager/CheckPackageAccess.java Tue Jun 16 17:05:08 2015 -0700 +++ b/jdk/test/java/lang/SecurityManager/CheckPackageAccess.java Wed Jun 17 15:47:12 2015 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2015, 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 @@ -29,12 +29,9 @@ * @run main/othervm CheckPackageAccess */ -import java.security.Security; import java.util.Collections; -import java.util.Arrays; import java.util.ArrayList; import java.util.List; -import java.util.StringTokenizer; /* * The main benefit of this test is to catch merge errors or other types @@ -44,60 +41,12 @@ */ public class CheckPackageAccess { - /* - * This array should be updated whenever new packages are added to the - * package.access property in the java.security file - * NOTE: it should be in the same order as the java.security file - */ - private static final String[] packages = { - "sun.", - "com.sun.xml.internal.", - "com.sun.imageio.", - "com.sun.istack.internal.", - "com.sun.jmx.", - "com.sun.media.sound.", - "com.sun.naming.internal.", - "com.sun.proxy.", - "com.sun.corba.se.", - "com.sun.org.apache.bcel.internal.", - "com.sun.org.apache.regexp.internal.", - "com.sun.org.apache.xerces.internal.", - "com.sun.org.apache.xpath.internal.", - "com.sun.org.apache.xalan.internal.extensions.", - "com.sun.org.apache.xalan.internal.lib.", - "com.sun.org.apache.xalan.internal.res.", - "com.sun.org.apache.xalan.internal.templates.", - "com.sun.org.apache.xalan.internal.utils.", - "com.sun.org.apache.xalan.internal.xslt.", - "com.sun.org.apache.xalan.internal.xsltc.cmdline.", - "com.sun.org.apache.xalan.internal.xsltc.compiler.", - "com.sun.org.apache.xalan.internal.xsltc.trax.", - "com.sun.org.apache.xalan.internal.xsltc.util.", - "com.sun.org.apache.xml.internal.res.", - "com.sun.org.apache.xml.internal.security.", - "com.sun.org.apache.xml.internal.serializer.utils.", - "com.sun.org.apache.xml.internal.utils.", - "com.sun.org.glassfish.", - "com.sun.tools.script.", - "com.oracle.xmlns.internal.", - "com.oracle.webservices.internal.", - "org.jcp.xml.dsig.internal.", - "jdk.internal.", - "jdk.nashorn.internal.", - "jdk.nashorn.tools.", - "jdk.tools.jimage.", - "com.sun.activation.registries." - }; + public static void main(String[] args) throws Exception { + // get expected list of restricted packages + List pkgs = RestrictedPackages.expected(); - public static void main(String[] args) throws Exception { - List pkgs = new ArrayList<>(Arrays.asList(packages)); - String osName = System.getProperty("os.name"); - if (osName.contains("OS X")) { - pkgs.add("apple."); // add apple package for OS X - } - - List jspkgs = - getPackages(Security.getProperty("package.access")); + // get actual list of restricted packages + List jspkgs = RestrictedPackages.actual(); if (!isOpenJDKOnly()) { String lastPkg = pkgs.get(pkgs.size() - 1); @@ -127,7 +76,7 @@ } System.setSecurityManager(new SecurityManager()); SecurityManager sm = System.getSecurityManager(); - for (String pkg : packages) { + for (String pkg : pkgs) { String subpkg = pkg + "foo"; try { sm.checkPackageAccess(pkg); @@ -153,18 +102,6 @@ System.out.println("Test passed"); } - private static List getPackages(String p) { - List packages = new ArrayList<>(); - if (p != null && !p.equals("")) { - StringTokenizer tok = new StringTokenizer(p, ","); - while (tok.hasMoreElements()) { - String s = tok.nextToken().trim(); - packages.add(s); - } - } - return packages; - } - private static boolean isOpenJDKOnly() { String prop = System.getProperty("java.runtime.name"); return prop != null && prop.startsWith("OpenJDK"); diff -r c8ceef082391 -r 316a8c3e572a jdk/test/java/lang/SecurityManager/CheckPackageMatching.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/SecurityManager/CheckPackageMatching.java Wed Jun 17 15:47:12 2015 -0400 @@ -0,0 +1,545 @@ +/* + * Copyright (c) 2015, 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 8072692 + * @summary Check the matching implemented by SecurityManager.checkPackageAccess + * @run main/othervm CheckPackageMatching + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/* + * The purpose of this test is not to verify the content of the package + * access list - but to ensure that the matching implemented by the + * SecurityManager is correct. This is why we have our own pattern matching + * algorithm here. + */ +public class CheckPackageMatching { + + /** + * The restricted packages listed in the package.access property of the + * java.security file. + */ + private static final String[] packages = + RestrictedPackages.actual().toArray(new String[0]); + + private static final boolean OPEN_JDK = isOpenJDKOnly(); + + /** + * PackageMatcher implements a state machine that matches package + * names against packages parsed from the package access list. + */ + private static abstract class PackageMatcher { + // For each state, chars[state] contains the chars that matches. + private final char[][] chars; + // For each state, states[state][i] contains the next state to go + // to when chars[state][i] matches the current character. + private final int[][] states; + + // Some markers. We're making the assumption that 0 + // cannot be a valid character for a package name. + // + // We use 0 for marking that we expect an end of string in + // char[state][i]. + private static final char END_OF_STRING = 0; + // This special state value indicates that we expect the string to end + // there. + private static final int END_STATE = -1; + // This special state value indicates that we can accept any character + // from now on. + private static final int WILDCARD_STATE = Integer.MIN_VALUE; + + // Create the data for a new state machine to match package names from + // the array of package names passed as argument. + // Each package name in the array is expected to end with '.' + // For each package in packages we're going to compile state data + // that will match the regexp: + // ^packages[i].substring(0, packages[i].length()-1).replace(".","\\.")$|^packages[i].replace(".","\\.").* + // + // Let's say the package array is: + // + // String[] packages = { "sun.", "com.sun.jmx.", "com.sun.proxy.", + // "apple." }; + // + // then the state machine will need data that looks like: + // + // char[][] chars = { + // { 'a', 'c', 's' }, { 'p' }, { 'p' }, { 'l' }, { 'e' }, { 0, '.' }, + // { 'o' }, { 'm' }, { '.' }, { 's' }, { 'u' }, { 'n' }, { '.' }, + // { 'j', 'p'}, + // { 'm' }, { 'x' }, { 0, '.' }, + // { 'r' }, { 'o' }, { 'x' }, { 'y' }, { 0, '.' }, + // { 'u' }, { 'n' }, { 0, '.' } + // } + // int[][] states = { + // { 1, 6, 22 }, { 2 }, { 3 }, { 4 }, { 5 }, + // { END_STATE, WILDCARD_STATE }, + // { 7 }, { 8 }, { 9 }, { 10 }, { 11 }, { 12 }, { 13 }, { 14, 17 }, + // { 15 }, { 16 }, { END_STATE, WILDCARD_STATE }, + // { 18 }, { 19 }, { 20 }, { 21 }, { END_STATE, WILDCARD_STATE }, + // { 23 }, { 24 }, { END_STATE, WILDCARD_STATE } + // } + // + // The machine will start by loading the chars and states for state 0 + // chars[0] => { 'a', 'c', 's' } states[0] => { 1, 6, 22 } + // then it examines the char at index 0 in the candidate name. + // if the char matches one of the characters in chars[0], then it goes + // to the corresponding state in states[0]. For instance - if the first + // char in the candidate name is 's', which corresponds to chars[0][2] - + // then it will proceed with the next char in the candidate name and go + // to state 22 (as indicated by states[0][2]) - where it will load the + // chars and states for states 22: chars[22] = { 'u' }, + // states[22] = { 23 } etc... until the candidate char at the current + // index matches no char in chars[states] => the candidate name doesn't + // match - or until it finds a success termination condition: the + // candidate chars are exhausted and states[state][0] is END_STATE, or + // the candidate chars are not exhausted - and + // states[state][chars[state]] is WILDCARD_STATE indicating a '.*' like + // regexp. + // + // [Note that the chars in chars[i] are sorted] + // + // The compile(...) method is reponsible for building the state machine + // data and is called only once in the constructor. + // + // The matches(String candidate) method will tell whether the candidate + // matches by implementing the algorithm described above. + // + PackageMatcher(String[] packages) { + final boolean[] selected = new boolean[packages.length]; + Arrays.fill(selected, true); + final ArrayList charList = new ArrayList<>(); + final ArrayList stateList = new ArrayList<>(); + compile(0, 0, packages, selected, charList, stateList); + chars = charList.toArray(new char[0][0]); + states = stateList.toArray(new int[0][0]); + } + + /** + * Compiles the state machine data (recursive). + * + * @param step The index of the character which we're looking at in + * this step. + * @param state The current state (starts at 0). + * @param pkgs The list of packages from which the automaton is built. + * @param selected Indicates which packages we're looking at in this + step. + * @param charList The list from which we will build + {@code char[][] chars;} + * @param stateList The list from which we will build + {@code int[][] states;} + * @return the next available state. + */ + private int compile(int step, int state, String[] pkgs, + boolean[] selected, ArrayList charList, + ArrayList stateList) { + final char[] next = new char[pkgs.length]; + final int[] nexti = new int[pkgs.length]; + int j = 0; + char min = Character.MAX_VALUE; char max = 0; + for (int i = 0; i < pkgs.length; i++) { + if (!selected[i]) continue; + final String p = pkgs[i]; + final int len = p.length(); + if (step > len) { + selected[i] = false; + continue; + } + if (len - 1 == step) { + boolean unknown = true; + for (int k = 0; k < j ; k++) { + if (next[k] == END_OF_STRING) { + unknown = false; + break; + } + } + if (unknown) { + next[j] = END_OF_STRING; + j++; + } + nexti[i] = END_STATE; + } + final char c = p.charAt(step); + nexti[i] = len - 1 == step ? END_STATE : c; + boolean unknown = j == 0 || c < min || c > max; + if (!unknown) { + if (c != min || c != max) { + unknown = true; + for (int k = 0; k < j ; k++) { + if (next[k] == c) { + unknown = false; + break; + } + } + } + } + if (unknown) { + min = min > c ? c : min; + max = max < c ? c : max; + next[j] = c; + j++; + } + } + final char[] nc = new char[j]; + final int[] nst = new int[j]; + System.arraycopy(next, 0, nc, 0, nc.length); + Arrays.sort(nc); + final boolean ns[] = new boolean[pkgs.length]; + + charList.ensureCapacity(state + 1); + stateList.ensureCapacity(state + 1); + charList.add(state, nc); + stateList.add(state, nst); + state = state + 1; + for (int k = 0; k < nc.length; k++) { + int selectedCount = 0; + boolean endStateFound = false; + boolean wildcardFound = false; + for (int l = 0; l < nexti.length; l++) { + if (!(ns[l] = selected[l])) { + continue; + } + ns[l] = nexti[l] == nc[k] || nexti[l] == END_STATE + && nc[k] == '.'; + endStateFound = endStateFound || nc[k] == END_OF_STRING + && nexti[l] == END_STATE; + wildcardFound = wildcardFound || nc[k] == '.' + && nexti[l] == END_STATE; + if (ns[l]) { + selectedCount++; + } + } + nst[k] = (endStateFound ? END_STATE + : wildcardFound ? WILDCARD_STATE : state); + if (selectedCount == 0 || wildcardFound) { + continue; + } + state = compile(step + 1, state, pkgs, ns, charList, stateList); + } + return state; + } + + /** + * Matches 'pkg' against the list of package names compiled in the + * state machine data. + * + * @param pkg The package name to match. Must not end with '.'. + * @return true if the package name matches, false otherwise. + */ + public boolean matches(String pkg) { + int state = 0; + int i; + final int len = pkg.length(); + next: for (i = 0; i <= len; i++) { + if (state == WILDCARD_STATE) { + return true; // all characters will match. + } + if (state == END_STATE) { + return i == len; + } + final char[] ch = chars[state]; + final int[] st = states[state]; + if (i == len) { + // matches only if we have exhausted the string. + return st[0] == END_STATE; + } + if (st[0] == END_STATE && st.length == 1) { + // matches only if we have exhausted the string. + return i == len; + } + final char c = pkg.charAt(i); // look at next char... + for (int j = st[0] == END_STATE ? 1 : 0; j < ch.length; j++) { + final char n = ch[j]; + if (c == n) { // found a match + state = st[j]; // get the next state. + continue next; // go to next state + } else if (c < n) { + break; // chars are sorted. we won't find it. no match. + } + } + break; // no match + } + return false; + } + } + + private static final class TestPackageMatcher extends PackageMatcher { + private final List list; + + TestPackageMatcher(String[] packages) { + super(packages); + this.list = Collections.unmodifiableList(Arrays.asList(packages)); + } + + @Override + public boolean matches(String pkg) { + final boolean match1 = super.matches(pkg); + boolean match2 = false; + String p2 = pkg + "."; + for (String p : list) { + if (pkg.startsWith(p) || p2.equals(p)) { + match2 = true; + break; + } + } + if (match1 != match2) { + System.err.println("Test Bug: PackageMatcher.matches(\"" + + pkg + "\") returned " + match1); + System.err.println("Package Access List is: " + list); + throw new Error("Test Bug: PackageMatcher.matches(\"" + + pkg + "\") returned " + match1); + } + return match1; + } + } + + private static void smokeTest() { + // these checks should pass. + System.getSecurityManager().checkPackageAccess("com.sun.blah"); + System.getSecurityManager().checkPackageAccess("com.sun.jm"); + System.getSecurityManager().checkPackageAccess("com.sun.jmxa"); + System.getSecurityManager().checkPackageAccess("jmx"); + List actual = Arrays.asList(packages); + for (String p : actual) { + if (!actual.contains(p)) { + System.err.println("Warning: '" + p + " not in package.access"); + } + } + if (!actual.contains("sun.")) { + throw new Error("package.access does not contain 'sun.'"); + } + } + + // This is a sanity test for our own test code. + private static void testTheTest(String[] pkgs, char[][] chars, + int[][] states) { + + PackageMatcher m = new TestPackageMatcher(pkgs); + String unexpected = ""; + if (!Arrays.deepEquals(chars, m.chars)) { + System.err.println("Char arrays differ"); + if (chars.length != m.chars.length) { + System.err.println("Char array lengths differ: expected=" + + chars.length + " actual=" + m.chars.length); + } + System.err.println(Arrays.deepToString(m.chars).replace((char)0, + '0')); + unexpected = "chars[]"; + } + if (!Arrays.deepEquals(states, m.states)) { + System.err.println("State arrays differ"); + if (states.length != m.states.length) { + System.err.println("Char array lengths differ: expected=" + + states.length + " actual=" + m.states.length); + } + System.err.println(Arrays.deepToString(m.states)); + if (unexpected.length() > 0) { + unexpected = unexpected + " and "; + } + unexpected = unexpected + "states[]"; + } + + if (unexpected.length() > 0) { + throw new Error("Unexpected "+unexpected+" in PackageMatcher"); + } + + testMatches(m, pkgs); + } + + // This is a sanity test for our own test code. + private static void testTheTest() { + final String[] packages2 = { "sun.", "com.sun.jmx.", + "com.sun.proxy.", "apple." }; + + final int END_STATE = PackageMatcher.END_STATE; + final int WILDCARD_STATE = PackageMatcher.WILDCARD_STATE; + + final char[][] chars2 = { + { 'a', 'c', 's' }, { 'p' }, { 'p' }, { 'l' }, { 'e' }, { 0, '.' }, + { 'o' }, { 'm' }, { '.' }, { 's' }, { 'u' }, { 'n' }, { '.' }, + { 'j', 'p'}, + { 'm' }, { 'x' }, { 0, '.' }, + { 'r' }, { 'o' }, { 'x' }, { 'y' }, { 0, '.' }, + { 'u' }, { 'n' }, { 0, '.' } + }; + + final int[][] states2 = { + { 1, 6, 22 }, { 2 }, { 3 }, { 4 }, { 5 }, + { END_STATE, WILDCARD_STATE }, + { 7 }, { 8 }, { 9 }, { 10 }, { 11 }, { 12 }, { 13 }, { 14, 17 }, + { 15 }, { 16 }, { END_STATE, WILDCARD_STATE }, + { 18 }, { 19 }, { 20 }, { 21 }, { END_STATE, WILDCARD_STATE }, + { 23 }, { 24 }, { END_STATE, WILDCARD_STATE } + }; + + testTheTest(packages2, chars2, states2); + + final String[] packages3 = { "sun.", "com.sun.pro.", + "com.sun.proxy.", "apple." }; + + final char[][] chars3 = { + { 'a', 'c', 's' }, { 'p' }, { 'p' }, { 'l' }, { 'e' }, { 0, '.' }, + { 'o' }, { 'm' }, { '.' }, { 's' }, { 'u' }, { 'n' }, { '.' }, + { 'p' }, { 'r' }, { 'o' }, { 0, '.', 'x' }, + { 'y' }, { 0, '.' }, + { 'u' }, { 'n' }, { 0, '.' } + }; + + final int[][] states3 = { + { 1, 6, 19 }, { 2 }, { 3 }, { 4 }, { 5 }, + { END_STATE, WILDCARD_STATE }, + { 7 }, { 8 }, { 9 }, { 10 }, { 11 }, { 12 }, { 13 }, { 14 }, + { 15 }, { 16 }, { END_STATE, WILDCARD_STATE, 17 }, + { 18 }, { END_STATE, WILDCARD_STATE }, + { 20 }, { 21 }, { END_STATE, WILDCARD_STATE } + }; + + testTheTest(packages3, chars3, states3); + } + + private static volatile boolean sanityTesting = false; + + public static void main(String[] args) { + System.setSecurityManager(new SecurityManager()); + + // Some smoke tests. + smokeTest(); + System.out.println("Smoke tests passed."); + + // Test our own pattern matching algorithm. Here we actually test + // the PackageMatcher class from our own test code. + sanityTesting = true; + try { + testTheTest(); + System.out.println("Sanity tests passed."); + } finally { + sanityTesting = false; + } + + // Now test the package matching in the security manager. + PackageMatcher matcher = new TestPackageMatcher(packages); + + // These should not match. + for (String pkg : new String[] {"gloups.machin", "su", + "org.jcp.xml.dsig.interna", + "com.sun.jm", "com.sun.jmxa"}) { + testMatch(matcher, pkg, false, true); + } + + // These should match. + for (String pkg : Arrays.asList( + new String[] {"sun.gloups.machin", "sun", "sun.com", + "com.sun.jmx", "com.sun.jmx.a", + "org.jcp.xml.dsig.internal", + "org.jcp.xml.dsig.internal.foo"})) { + testMatch(matcher, pkg, true, true); + } + + // Derive a list of packages that should match or not match from + // the list in 'packages' - and check that the security manager + // throws the appropriate exception. + testMatches(matcher, packages); + } + + private static void testMatches(PackageMatcher matcher, String[] pkgs) { + Collection pkglist = Arrays.asList(pkgs); + PackageMatcher ref = new TestPackageMatcher(packages); + + for (String pkg : pkgs) { + String candidate = pkg + "toto"; + boolean expected = true; + testMatch(matcher, candidate, expected, + ref.matches(candidate) == expected); + } + + for (String pkg : pkgs) { + String candidate = pkg.substring(0, pkg.length() - 1); + boolean expected = pkglist.contains(candidate + "."); + testMatch(matcher, candidate, expected, + ref.matches(candidate) == expected); + } + + for (String pkg : pkgs) { + if (!OPEN_JDK && pkg.equals("com.sun.media.sound.")) { + // don't test com.sun.media.sound since there is an entry + // for com.sun.media in non OpenJDK builds. Otherwise, + // the test for this package will fail unexpectedly. + continue; + } + String candidate = pkg.substring(0, pkg.length() - 2); + boolean expected = pkglist.contains(candidate + "."); + testMatch(matcher, candidate, expected, + ref.matches(candidate) == expected); + } + } + + private static void testMatch(PackageMatcher matcher, String candidate, + boolean expected, boolean testSecurityManager) + { + final boolean m = matcher.matches(candidate); + if (m != expected) { + final String msg = "\"" + candidate + "\": " + + (m ? "matches" : "does not match"); + throw new Error("PackageMatcher does not give expected results: " + + msg); + } + + if (sanityTesting) { + testSecurityManager = false; + } + + if (testSecurityManager) { + System.out.println("Access to " + candidate + " should be " + + (expected ? "rejected" : "granted")); + final String errormsg = "\"" + candidate + "\" : " + + (expected ? "granted" : "not granted"); + try { + System.getSecurityManager().checkPackageAccess(candidate); + if (expected) { + System.err.println(errormsg); + throw new Error("Expected exception not thrown: " + + errormsg); + } + } catch (SecurityException x) { + if (!expected) { + System.err.println(errormsg); + throw new Error(errormsg + " - unexpected exception: " + + x, x); + } else { + System.out.println("Got expected exception: " + x); + } + } + } + } + + private static boolean isOpenJDKOnly() { + String prop = System.getProperty("java.runtime.name"); + return prop != null && prop.startsWith("OpenJDK"); + } +} diff -r c8ceef082391 -r 316a8c3e572a jdk/test/java/lang/SecurityManager/RestrictedPackages.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/SecurityManager/RestrictedPackages.java Wed Jun 17 15:47:12 2015 -0400 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, 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.security.Security; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.StringTokenizer; + +/** + * A collection of utility methods and constants for testing the package + * access and package definition security checks. + */ +final class RestrictedPackages { + + /* + * The expected list of restricted packages. + * + * This array should be updated whenever new packages are added to the + * package.access property in the java.security file + * NOTE: it should be in the same order as the java.security file + */ + static final String[] EXPECTED = { + "sun.", + "com.sun.xml.internal.", + "com.sun.imageio.", + "com.sun.istack.internal.", + "com.sun.jmx.", + "com.sun.media.sound.", + "com.sun.naming.internal.", + "com.sun.proxy.", + "com.sun.corba.se.", + "com.sun.org.apache.bcel.internal.", + "com.sun.org.apache.regexp.internal.", + "com.sun.org.apache.xerces.internal.", + "com.sun.org.apache.xpath.internal.", + "com.sun.org.apache.xalan.internal.extensions.", + "com.sun.org.apache.xalan.internal.lib.", + "com.sun.org.apache.xalan.internal.res.", + "com.sun.org.apache.xalan.internal.templates.", + "com.sun.org.apache.xalan.internal.utils.", + "com.sun.org.apache.xalan.internal.xslt.", + "com.sun.org.apache.xalan.internal.xsltc.cmdline.", + "com.sun.org.apache.xalan.internal.xsltc.compiler.", + "com.sun.org.apache.xalan.internal.xsltc.trax.", + "com.sun.org.apache.xalan.internal.xsltc.util.", + "com.sun.org.apache.xml.internal.res.", + "com.sun.org.apache.xml.internal.security.", + "com.sun.org.apache.xml.internal.serializer.utils.", + "com.sun.org.apache.xml.internal.utils.", + "com.sun.org.glassfish.", + "com.sun.tools.script.", + "com.oracle.xmlns.internal.", + "com.oracle.webservices.internal.", + "org.jcp.xml.dsig.internal.", + "jdk.internal.", + "jdk.nashorn.internal.", + "jdk.nashorn.tools.", + "jdk.tools.jimage.", + "com.sun.activation.registries." + }; + + /* + * A non-exhaustive list of restricted packages. + * + * Contrary to what is in the EXPECTED list, this list does not need + * to be exhaustive. + */ + static final String[] EXPECTED_NONEXHAUSTIVE = { + "sun.", + "com.sun.xml.internal.", + "com.sun.imageio.", + "com.sun.istack.internal.", + "com.sun.jmx.", + "com.sun.proxy.", + "com.sun.org.apache.bcel.internal.", + "com.sun.org.apache.regexp.internal.", + "com.sun.org.apache.xerces.internal.", + "com.sun.org.apache.xpath.internal.", + "com.sun.org.apache.xalan.internal.extensions.", + "com.sun.org.apache.xalan.internal.lib.", + "com.sun.org.apache.xalan.internal.res.", + "com.sun.org.apache.xalan.internal.templates.", + "com.sun.org.apache.xalan.internal.utils.", + "com.sun.org.apache.xalan.internal.xslt.", + "com.sun.org.apache.xalan.internal.xsltc.cmdline.", + "com.sun.org.apache.xalan.internal.xsltc.compiler.", + "com.sun.org.apache.xalan.internal.xsltc.trax.", + "com.sun.org.apache.xalan.internal.xsltc.util.", + "com.sun.org.apache.xml.internal.res.", + "com.sun.org.apache.xml.internal.serializer.utils.", + "com.sun.org.apache.xml.internal.utils.", + "com.sun.org.apache.xml.internal.security.", + "com.sun.org.glassfish.", + "org.jcp.xml.dsig.internal." + }; + + private static final String OS_NAME = System.getProperty("os.name"); + + /** + * Returns a list of expected restricted packages, including any + * OS specific packages. The returned list is mutable. + */ + static List expected() { + List pkgs = new ArrayList<>(Arrays.asList(EXPECTED)); + if (OS_NAME.contains("OS X")) { + pkgs.add("apple."); // add apple package for OS X + } + return pkgs; + } + + /** + * Returns a list of actual restricted packages. The returned list + * is mutable. + */ + static List actual() { + String prop = Security.getProperty("package.access"); + List packages = new ArrayList<>(); + if (prop != null && !prop.equals("")) { + StringTokenizer tok = new StringTokenizer(prop, ","); + while (tok.hasMoreElements()) { + String s = tok.nextToken().trim(); + packages.add(s); + } + } + return packages; + } + + private RestrictedPackages() { } +}