1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 import java.io.File; 24 import java.io.FilePermission; 25 import java.io.IOException; 26 import java.lang.reflect.Field; 27 import java.lang.reflect.ReflectPermission; 28 import java.security.CodeSource; 29 import java.security.Permission; 30 import java.security.PermissionCollection; 31 import java.security.Permissions; 32 import java.security.Policy; 33 import java.security.ProtectionDomain; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.Enumeration; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.PropertyPermission; 41 import java.util.concurrent.atomic.AtomicBoolean; 42 import java.util.concurrent.atomic.AtomicLong; 43 import java.util.jar.JarEntry; 44 import java.util.jar.JarFile; 45 46 /** 47 * @test 48 * @bug 8065552 49 * @summary test that all fields returned by getDeclaredFields() can be 50 * set accessible if the right permission is granted; this test 51 * loads all the classes in the BCL, get their declared fields, 52 * and call setAccessible(false) followed by setAccessible(true); 53 * This test has several (at)run main/othervm lines to work around the fact 54 * that the default PermGen size is usually too small to load all classes. 55 * So we split the list in batch of ~ 4000 and run the test 56 * once for each batch. The first number is the start index, 57 * the second is the maximum number of classes to load. 58 * 59 * @run main/othervm FieldSetAccessibleTest UNSECURE 0 4000 60 * @run main/othervm FieldSetAccessibleTest SECURE 0 4000 61 * @run main/othervm FieldSetAccessibleTest UNSECURE 4000 4000 62 * @run main/othervm FieldSetAccessibleTest SECURE 4000 4000 63 * @run main/othervm FieldSetAccessibleTest UNSECURE 8000 4000 64 * @run main/othervm FieldSetAccessibleTest SECURE 8000 4000 65 * @run main/othervm FieldSetAccessibleTest UNSECURE 12000 4000 66 * @run main/othervm FieldSetAccessibleTest SECURE 12000 4000 67 * @run main/othervm FieldSetAccessibleTest UNSECURE 16000 4000 68 * @run main/othervm FieldSetAccessibleTest SECURE 16000 4000 69 * @run main/othervm FieldSetAccessibleTest UNSECURE 20000 70 * @run main/othervm FieldSetAccessibleTest SECURE 20000 71 * 72 * @author danielfuchs 73 */ 74 75 76 public class FieldSetAccessibleTest { 77 78 static final List<String> skipped = new ArrayList<>(); 79 static final List<String> cantread = new ArrayList<>(); 80 static final List<String> failed = new ArrayList<>(); 81 static final AtomicLong classCount = new AtomicLong(); 82 static final AtomicLong fieldCount = new AtomicLong(); 83 static long startIndex = 0; 84 static long maxSize = Long.MAX_VALUE; 85 static long maxIndex = Long.MAX_VALUE; 86 87 88 static void testSetFieldsAccessible(Class<?> c) { 89 for (Field f : c.getDeclaredFields()) { 90 fieldCount.incrementAndGet(); 91 f.setAccessible(false); 92 f.setAccessible(true); 93 } 94 } 95 96 97 public static boolean test(Class<?> c) { 98 //System.out.println(c.getName()); 99 classCount.incrementAndGet(); 100 101 // Call getDeclaredFields() and try to set their accessible flag. 102 testSetFieldsAccessible(c); 103 104 // add more tests here... 105 106 return c == Class.class; 107 } 108 109 static ClassLoader getClassLoaderFor(String classFileName) { 110 return null; // load all classes through the BCL. 111 } 112 113 static void checkClassLoaderFor(Class<?> c, ClassLoader loader) { 114 if (loader == null) { // expects BCL 115 if (c.getClassLoader() != loader) { 116 System.err.println("Unexpected loader for "+c+": "+c.getClassLoader()); 117 } 118 } 119 } 120 121 static void printSummary(long secs, long millis, long nanos) { 122 System.out.println("Tested " + fieldCount.get() + " fields of " 123 + classCount.get() + " classes in " 124 + secs + "s " + millis + "ms " + nanos + "ns"); 125 } 126 127 128 /** 129 * @param args the command line arguments: 130 * 131 * SECURE|UNSECURE [startIndex (default=0)] [maxSize (default=Long.MAX_VALUE)] 132 * 133 * @throws java.lang.Exception if the test fails 134 */ 135 public static void main(String[] args) throws Exception { 136 if (args == null || args.length == 0) { 137 args = new String[] {"SECURE", "0"}; 138 } else if (args.length > 3) { 139 throw new RuntimeException("Expected at most one argument. Found " 140 + Arrays.asList(args)); 141 } 142 try { 143 if (args.length > 1) { 144 startIndex = Long.parseLong(args[1]); 145 if (startIndex < 0) { 146 throw new IllegalArgumentException("startIndex args[1]: " 147 + startIndex); 148 } 149 } 150 if (args.length > 2) { 151 maxSize = Long.parseLong(args[2]); 152 if (maxSize <= 0) { 153 maxSize = Long.MAX_VALUE; 154 } 155 maxIndex = (Long.MAX_VALUE - startIndex) < maxSize 156 ? Long.MAX_VALUE : startIndex + maxSize; 157 } 158 TestCase.valueOf(args[0]).run(); 159 } catch (OutOfMemoryError oome) { 160 System.err.println(classCount.get()); 161 throw oome; 162 } 163 } 164 165 public static void run(TestCase test) { 166 System.out.println("Testing " + test); 167 test(listAllClassNames()); 168 System.out.println("Passed " + test); 169 } 170 171 static Iterable<String> listAllClassNames() { 172 boolean sort = startIndex != 0 || maxIndex != Long.MAX_VALUE; 173 return new ClassNameListBuilder(sort); 174 } 175 176 static void test(Iterable<String> iterable) { 177 final long start = System.nanoTime(); 178 boolean classFound = false; 179 int index = 0; 180 for (String s: iterable) { 181 if (index == maxIndex) break; 182 try { 183 if (index < startIndex) continue; 184 if (test(getClassLoaderFor(s), s)) { 185 classFound = true; 186 } 187 } finally { 188 index++; 189 } 190 } 191 long elapsed = System.nanoTime() - start; 192 long secs = elapsed / 1000_000_000; 193 long millis = (elapsed % 1000_000_000) / 1000_000; 194 long nanos = elapsed % 1000_000; 195 System.out.println("Unreadable path elements: " + cantread); 196 System.out.println("Skipped path elements: " + skipped); 197 System.out.println("Failed path elements: " + failed); 198 printSummary(secs, millis, nanos); 199 200 if (!failed.isEmpty()) { 201 throw new RuntimeException("Test failed for the following classes: " + failed); 202 } 203 if (!classFound && startIndex == 0 && index < maxIndex) { 204 // this is just to verify that we have indeed parsed rt.jar 205 // (or the java.base module) 206 throw new RuntimeException("Test failed: Class.class not found..."); 207 } 208 if (classCount.get() == 0 && startIndex < 20000) { 209 throw new RuntimeException("Test failed: no class found?"); 210 } 211 } 212 213 static boolean test(ClassLoader loader, String s) { 214 try { 215 if (s.startsWith("WrapperGenerator")) { 216 System.out.println("Skipping "+ s); 217 return false; 218 } 219 final Class<?> c = Class.forName( 220 s.replace('/', '.').substring(0, s.length() - 6), 221 false, 222 loader); 223 checkClassLoaderFor(c, loader); 224 return test(c); 225 } catch (Exception t) { 226 t.printStackTrace(); 227 failed.add(s); 228 } 229 return false; 230 } 231 232 233 static class ClassNameListBuilder implements Iterable<String> { 234 String[] bcp; 235 boolean sort; 236 ClassNameListBuilder(boolean sort) { 237 bcp = System.getProperty("sun.boot.class.path").split(File.pathSeparator); 238 this.sort = sort; 239 } 240 241 public List<String> build() { 242 return findClasses(); 243 } 244 245 List<String> findClasses() { 246 List<String> list = new ArrayList<>(); 247 248 for (String s : bcp) { 249 System.out.println("boot class path element: " + s); 250 if (s.endsWith(".jar")) { 251 addJarEntries(s, list); 252 } else { 253 addFolderEntries(new File(s), s, list); 254 } 255 } 256 257 if (sort) { 258 System.out.println("Sorting class names..."); 259 Collections.sort(list); 260 System.out.println("\t... Sorted."); 261 } 262 return list; 263 } 264 265 void addJarEntries(String jarName, List<String> classNames) { 266 File f = new File(jarName); 267 if (f.canRead() && f.isFile()) { 268 try { 269 JarFile jarFile = new JarFile(f); 270 for (Enumeration<JarEntry> entries = jarFile.entries() ; entries.hasMoreElements() ;) { 271 JarEntry e = entries.nextElement(); 272 if (e.isDirectory()) { 273 continue; 274 } 275 if (e.getName().endsWith(".class")) { 276 classNames.add(e.getName()); 277 } 278 } 279 } catch (IOException e) { 280 e.printStackTrace(); 281 skipped.add(jarName); 282 } 283 } else { 284 cantread.add(jarName); 285 } 286 } 287 288 void addFolderEntries(File root, String folderName, List<String> classNames) { 289 File f = new File(folderName); 290 if (f.canRead()) { 291 if (f.isDirectory()) { 292 for (String child : f.list()) { 293 addFolderEntries(root, new File(f,child).getPath(), classNames); 294 } 295 } else if (f.isFile() && f.getName().endsWith(".class")) { 296 String className = root.toPath().relativize(f.toPath()).toString() 297 .replace(File.separatorChar, '/'); 298 System.out.println("adding: " + className); 299 classNames.add(className); 300 } 301 } else { 302 cantread.add(folderName); 303 } 304 } 305 306 @Override 307 public Iterator<String> iterator() { 308 return build().iterator(); 309 } 310 311 } 312 313 // Test with or without a security manager 314 public static enum TestCase { 315 UNSECURE, SECURE; 316 public void run() throws Exception { 317 System.out.println("Running test case: " + name()); 318 Configure.setUp(this); 319 FieldSetAccessibleTest.run(this); 320 } 321 } 322 323 // A helper class to configure the security manager for the test, 324 // and bypass it when needed. 325 static class Configure { 326 static Policy policy = null; 327 static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() { 328 @Override 329 protected AtomicBoolean initialValue() { 330 return new AtomicBoolean(false); 331 } 332 }; 333 static void setUp(TestCase test) { 334 switch (test) { 335 case SECURE: 336 if (policy == null && System.getSecurityManager() != null) { 337 throw new IllegalStateException("SecurityManager already set"); 338 } else if (policy == null) { 339 policy = new SimplePolicy(TestCase.SECURE, allowAll); 340 Policy.setPolicy(policy); 341 System.setSecurityManager(new SecurityManager()); 342 } 343 if (System.getSecurityManager() == null) { 344 throw new IllegalStateException("No SecurityManager."); 345 } 346 if (policy == null) { 347 throw new IllegalStateException("policy not configured"); 348 } 349 break; 350 case UNSECURE: 351 if (System.getSecurityManager() != null) { 352 throw new IllegalStateException("SecurityManager already set"); 353 } 354 break; 355 default: 356 throw new InternalError("No such testcase: " + test); 357 } 358 } 359 static void doPrivileged(Runnable run) { 360 allowAll.get().set(true); 361 try { 362 run.run(); 363 } finally { 364 allowAll.get().set(false); 365 } 366 } 367 } 368 369 // A Helper class to build a set of permissions. 370 final static class PermissionsBuilder { 371 final Permissions perms; 372 public PermissionsBuilder() { 373 this(new Permissions()); 374 } 375 public PermissionsBuilder(Permissions perms) { 376 this.perms = perms; 377 } 378 public PermissionsBuilder add(Permission p) { 379 perms.add(p); 380 return this; 381 } 382 public PermissionsBuilder addAll(PermissionCollection col) { 383 if (col != null) { 384 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) { 385 perms.add(e.nextElement()); 386 } 387 } 388 return this; 389 } 390 public Permissions toPermissions() { 391 final PermissionsBuilder builder = new PermissionsBuilder(); 392 builder.addAll(perms); 393 return builder.perms; 394 } 395 } 396 397 // Policy for the test... 398 public static class SimplePolicy extends Policy { 399 400 final Permissions permissions; 401 final Permissions allPermissions; 402 final ThreadLocal<AtomicBoolean> allowAll; 403 public SimplePolicy(TestCase test, ThreadLocal<AtomicBoolean> allowAll) { 404 this.allowAll = allowAll; 405 406 // Permission needed by the tested code exercised in the test 407 permissions = new Permissions(); 408 permissions.add(new RuntimePermission("createClassLoader")); 409 permissions.add(new RuntimePermission("closeClassLoader")); 410 permissions.add(new RuntimePermission("getClassLoader")); 411 permissions.add(new RuntimePermission("accessDeclaredMembers")); 412 permissions.add(new ReflectPermission("suppressAccessChecks")); 413 permissions.add(new PropertyPermission("sun.boot.class.path", "read")); 414 permissions.add(new FilePermission("<<ALL FILES>>", "read")); 415 416 // these are used for configuring the test itself... 417 allPermissions = new Permissions(); 418 allPermissions.add(new java.security.AllPermission()); 419 } 420 421 @Override 422 public boolean implies(ProtectionDomain domain, Permission permission) { 423 if (allowAll.get().get()) return allPermissions.implies(permission); 424 if (permissions.implies(permission)) return true; 425 if (permission instanceof java.lang.RuntimePermission) { 426 if (permission.getName().startsWith("accessClassInPackage.")) { 427 // add these along to the set of permission we have, when we 428 // discover that we need them. 429 permissions.add(permission); 430 return true; 431 } 432 } 433 return false; 434 } 435 436 @Override 437 public PermissionCollection getPermissions(CodeSource codesource) { 438 return new PermissionsBuilder().addAll(allowAll.get().get() 439 ? allPermissions : permissions).toPermissions(); 440 } 441 442 @Override 443 public PermissionCollection getPermissions(ProtectionDomain domain) { 444 return new PermissionsBuilder().addAll(allowAll.get().get() 445 ? allPermissions : permissions).toPermissions(); 446 } 447 } 448 449 }