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 24 import java.io.File; 25 import java.io.FilePermission; 26 import java.io.IOException; 27 import java.io.UncheckedIOException; 28 import java.lang.reflect.Field; 29 import java.lang.reflect.ReflectPermission; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.nio.file.Paths; 33 import java.security.CodeSource; 34 import java.security.Permission; 35 import java.security.PermissionCollection; 36 import java.security.Permissions; 37 import java.security.Policy; 38 import java.security.ProtectionDomain; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.Enumeration; 43 import java.util.Iterator; 44 import java.util.List; 45 import java.util.PropertyPermission; 46 import java.util.Set; 47 import java.util.TreeSet; 48 import java.util.concurrent.atomic.AtomicBoolean; 49 import java.util.concurrent.atomic.AtomicLong; 50 import java.util.stream.Collectors; 51 import java.util.stream.Stream; 52 import jdk.internal.jimage.BasicImageReader; 53 54 /** 55 * @test 56 * @bug 8065552 57 * @summary test that all fields returned by getDeclaredFields() can be 58 * set accessible if the right permission is granted; this test 59 * loads all the classes in the BCL, get their declared fields, 60 * and call setAccessible(false) followed by setAccessible(true); 61 * @run main/othervm FieldSetAccessibleTest UNSECURE 62 * @run main/othervm FieldSetAccessibleTest SECURE 63 * 64 * @author danielfuchs 65 */ 66 public class FieldSetAccessibleTest { 67 68 static final List<String> skipped = new ArrayList<>(); 69 static final List<String> cantread = new ArrayList<>(); 70 static final List<String> failed = new ArrayList<>(); 71 static final AtomicLong classCount = new AtomicLong(); 72 static final AtomicLong fieldCount = new AtomicLong(); 73 static long startIndex = 0; 74 static long maxSize = Long.MAX_VALUE; 75 static long maxIndex = Long.MAX_VALUE; 76 final static boolean restrictToBoot = Boolean.parseBoolean( 77 System.getProperty("test.boot.only", "false")); 78 final static boolean listClassesByLoader = Boolean.parseBoolean( 79 System.getProperty("test.list.classes", "true")); 80 81 82 static void testSetFieldsAccessible(Class<?> c) { 83 for (Field f : c.getDeclaredFields()) { 84 fieldCount.incrementAndGet(); 85 f.setAccessible(false); 86 f.setAccessible(true); 87 } 88 } 89 90 91 public static boolean test(Class<?> c) { 92 //System.out.println(c.getName()); 93 classCount.incrementAndGet(); 94 95 // Call getDeclaredFields() and try to set their accessible flag. 96 testSetFieldsAccessible(c); 97 98 // add more tests here... 99 100 return c == Class.class; 101 } 102 103 104 static ClassLoader getClassLoaderFor(String classFileName) { 105 if (restrictToBoot) return null; // only bootmodules 106 return ClassLoaders.systemClassLoader; 107 } 108 109 static void checkClassLoaderFor(Class<?> c, ClassLoader loader) { 110 ClassLoaders.checkFor(c, loader); 111 } 112 113 static void printSummary(long secs, long millis, long nanos) { 114 ClassLoaders.printClasses(); 115 System.out.println("Tested " + fieldCount.get() + " fields of " 116 + classCount.get() + " classes in " 117 + secs + "s " + millis + "ms " + nanos + "ns"); 118 } 119 120 121 static class ClassLoaders { 122 final static ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 123 final static ClassLoader extensionClassLoader = systemClassLoader.getParent(); 124 final static Set<String> bootClasses = new TreeSet<>(); 125 final static Set<String> applicationClasses = new TreeSet<>(); 126 final static Set<String> extensionClasses = new TreeSet<>(); 127 128 static ClassLoader getFor(String classFileName) { 129 return systemClassLoader; 130 } 131 static void checkFor(Class<?> c, ClassLoader loader) { 132 final ClassLoader cl = c.getClassLoader(); 133 if (cl != null) { 134 if (restrictToBoot || (cl != systemClassLoader && cl != extensionClassLoader)) { 135 System.err.println("Unexpected loader for "+c+": "+c.getClassLoader()); 136 } 137 if (cl == extensionClassLoader) { 138 extensionClasses.add(c.getName()); 139 } 140 if (cl == systemClassLoader) { 141 applicationClasses.add(c.getName()); 142 } 143 } else { 144 bootClasses.add(c.getName()); 145 } 146 } 147 148 static void printClasses() { 149 if (listClassesByLoader) { 150 System.out.println("\n------------- Boot Classes -------------\n"); 151 bootClasses.stream().forEachOrdered(s -> System.out.println(s)); 152 System.out.println("\n------------- Extension Classes -------------\n"); 153 extensionClasses.stream().forEachOrdered(s -> System.out.println(s)); 154 System.out.println("\n------------- Application Classes -------------\n"); 155 applicationClasses.stream().forEachOrdered(s -> System.out.println(s)); 156 System.out.println("\n-----------------------------------------------\n"); 157 } 158 } 159 } 160 161 /** 162 * @param args the command line arguments: 163 * 164 * SECURE|UNSECURE [startIndex (default=0)] [maxSize (default=Long.MAX_VALUE)] 165 * 166 * @throws java.lang.Exception if the test fails 167 */ 168 public static void main(String[] args) throws Exception { 169 if (args == null || args.length == 0) { 170 args = new String[] {"SECURE", "0"}; 171 } else if (args.length > 3) { 172 throw new RuntimeException("Expected at most one argument. Found " 173 + Arrays.asList(args)); 174 } 175 try { 176 if (args.length > 1) { 177 startIndex = Long.parseLong(args[1]); 178 if (startIndex < 0) { 179 throw new IllegalArgumentException("startIndex args[1]: " 180 + startIndex); 181 } 182 } 183 if (args.length > 2) { 184 maxSize = Long.parseLong(args[2]); 185 if (maxSize <= 0) { 186 maxSize = Long.MAX_VALUE; 187 } 188 maxIndex = (Long.MAX_VALUE - startIndex) < maxSize 189 ? Long.MAX_VALUE : startIndex + maxSize; 190 } 191 TestCase.valueOf(args[0]).run(); 192 } catch (OutOfMemoryError oome) { 193 System.err.println(classCount.get()); 194 throw oome; 195 } 196 } 197 198 public static void run(TestCase test) { 199 System.out.println("Testing " + test); 200 test(listAllClassNames()); 201 System.out.println("Passed " + test); 202 } 203 204 static Iterable<String> listAllClassNames() { 205 return new ClassNameJImageStreamBuilder(); 206 } 207 208 static void test(Iterable<String> iterable) { 209 final long start = System.nanoTime(); 210 boolean classFound = false; 211 int index = 0; 212 for (String s: iterable) { 213 if (index == maxIndex) break; 214 try { 215 if (index < startIndex) continue; 216 if (test(getClassLoaderFor(s), s)) { 217 classFound = true; 218 } 219 } finally { 220 index++; 221 } 222 } 223 long elapsed = System.nanoTime() - start; 224 long secs = elapsed / 1000_000_000; 225 long millis = (elapsed % 1000_000_000) / 1000_000; 226 long nanos = elapsed % 1000_000; 227 System.out.println("Unreadable path elements: " + cantread); 228 System.out.println("Skipped path elements: " + skipped); 229 System.out.println("Failed path elements: " + failed); 230 printSummary(secs, millis, nanos); 231 232 if (!failed.isEmpty()) { 233 throw new RuntimeException("Test failed for the following classes: " + failed); 234 } 235 if (!classFound && startIndex == 0 && index < maxIndex) { 236 // this is just to verify that we have indeed parsed rt.jar 237 // (or the java.base module) 238 throw new RuntimeException("Test failed: Class.class not found..."); 239 } 240 if (classCount.get() == 0 && startIndex == 0) { 241 throw new RuntimeException("Test failed: no class found?"); 242 } 243 } 244 245 static boolean test(ClassLoader loader, String s) { 246 try { 247 if (s.startsWith("WrapperGenerator")) { 248 System.out.println("Skipping "+ s); 249 return false; 250 } 251 final Class<?> c = Class.forName( 252 s.replace('/', '.').substring(0, s.length() - 6), 253 false, 254 loader); 255 checkClassLoaderFor(c, loader); 256 return test(c); 257 } catch (Exception t) { 258 t.printStackTrace(System.err); 259 failed.add(s); 260 } 261 return false; 262 } 263 264 static class ClassNameJImageStreamBuilder implements Iterable<String>{ 265 266 final String[] images; 267 268 ClassNameJImageStreamBuilder() { 269 images = imageFiles(); 270 } 271 272 static String[] imageFiles() { 273 if (restrictToBoot) { 274 return System.getProperty("sun.boot.class.path").split(File.pathSeparator); 275 } else { 276 Path modules = Paths.get(System.getProperty("java.home"), "lib", "modules"); 277 try { 278 return Files.walk(modules) 279 .filter(p -> p.getFileName().toString().endsWith(".jimage")) 280 .map(p -> p.toString()) 281 .collect(Collectors.toList()).toArray(new String[3]); 282 } catch (IOException x) { 283 throw new UncheckedIOException(x); 284 } 285 } 286 } 287 288 Stream<String> build() { 289 return Arrays.stream(images) 290 .filter(x -> x.endsWith(".jimage")) 291 .flatMap(this::toJImageReader).flatMap(r -> Arrays.stream(r.getEntryNames())) 292 .filter(n -> n.endsWith(".class")); 293 } 294 295 @Override 296 public Iterator<String> iterator() { 297 return build().iterator(); 298 } 299 300 private Stream<JImageReader> toJImageReader(String imageName) { 301 try { 302 return Stream.of(new JImageReader(Paths.get(imageName))); 303 } catch(IOException x) { 304 x.printStackTrace(System.err); 305 skipped.add(imageName); 306 } 307 return Collections.<JImageReader>emptyList().stream(); 308 } 309 310 // private List<JImageReader> newJImageReaders() throws IOException { 311 // String home = System.getProperty("java.home"); 312 // Path mlib = Paths.get(home, "lib", "modules"); 313 // try (Stream<Path> paths = Files.list(mlib)) { 314 // Set<Path> jimages = paths.filter(p -> p.toString().endsWith(".jimage")) 315 // .collect(Collectors.toSet()); 316 // List<JImageReader> result = new ArrayList<>(); 317 // for (Path jimage : jimages) { 318 // result.add(new JImageReader(jimage)); 319 // System.out.println("opened " + jimage); 320 // } 321 // return result; 322 // } 323 // } 324 325 static class JImageReader extends BasicImageReader { 326 327 final Path jimage; 328 329 JImageReader(Path p) throws IOException { 330 super(p.toString()); 331 System.out.println("JImageReader: " + p.toString()); 332 this.jimage = p; 333 } 334 335 String imageName() { 336 return jimage.getFileName().toString(); 337 } 338 339 int entries() { 340 return getHeader().getLocationCount(); 341 } 342 } 343 } 344 345 // Test with or without a security manager 346 public static enum TestCase { 347 UNSECURE, SECURE; 348 public void run() throws Exception { 349 System.out.println("Running test case: " + name()); 350 Configure.setUp(this); 351 FieldSetAccessibleTest.run(this); 352 } 353 } 354 355 // A helper class to configure the security manager for the test, 356 // and bypass it when needed. 357 static class Configure { 358 static Policy policy = null; 359 static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() { 360 @Override 361 protected AtomicBoolean initialValue() { 362 return new AtomicBoolean(false); 363 } 364 }; 365 static void setUp(TestCase test) { 366 switch (test) { 367 case SECURE: 368 if (policy == null && System.getSecurityManager() != null) { 369 throw new IllegalStateException("SecurityManager already set"); 370 } else if (policy == null) { 371 policy = new SimplePolicy(TestCase.SECURE, allowAll); 372 Policy.setPolicy(policy); 373 System.setSecurityManager(new SecurityManager()); 374 } 375 if (System.getSecurityManager() == null) { 376 throw new IllegalStateException("No SecurityManager."); 377 } 378 if (policy == null) { 379 throw new IllegalStateException("policy not configured"); 380 } 381 break; 382 case UNSECURE: 383 if (System.getSecurityManager() != null) { 384 throw new IllegalStateException("SecurityManager already set"); 385 } 386 break; 387 default: 388 throw new InternalError("No such testcase: " + test); 389 } 390 } 391 static void doPrivileged(Runnable run) { 392 allowAll.get().set(true); 393 try { 394 run.run(); 395 } finally { 396 allowAll.get().set(false); 397 } 398 } 399 } 400 401 // A Helper class to build a set of permissions. 402 final static class PermissionsBuilder { 403 final Permissions perms; 404 public PermissionsBuilder() { 405 this(new Permissions()); 406 } 407 public PermissionsBuilder(Permissions perms) { 408 this.perms = perms; 409 } 410 public PermissionsBuilder add(Permission p) { 411 perms.add(p); 412 return this; 413 } 414 public PermissionsBuilder addAll(PermissionCollection col) { 415 if (col != null) { 416 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) { 417 perms.add(e.nextElement()); 418 } 419 } 420 return this; 421 } 422 public Permissions toPermissions() { 423 final PermissionsBuilder builder = new PermissionsBuilder(); 424 builder.addAll(perms); 425 return builder.perms; 426 } 427 } 428 429 // Policy for the test... 430 public static class SimplePolicy extends Policy { 431 432 final Permissions permissions; 433 final Permissions allPermissions; 434 final ThreadLocal<AtomicBoolean> allowAll; 435 public SimplePolicy(TestCase test, ThreadLocal<AtomicBoolean> allowAll) { 436 this.allowAll = allowAll; 437 438 // Permission needed by the tested code exercised in the test 439 permissions = new Permissions(); 440 permissions.add(new RuntimePermission("createClassLoader")); 441 permissions.add(new RuntimePermission("closeClassLoader")); 442 permissions.add(new RuntimePermission("getClassLoader")); 443 permissions.add(new RuntimePermission("accessDeclaredMembers")); 444 permissions.add(new ReflectPermission("suppressAccessChecks")); 445 permissions.add(new PropertyPermission("sun.boot.class.path", "read")); 446 permissions.add(new PropertyPermission("java.home", "read")); 447 permissions.add(new PropertyPermission("test.list.classes", "read")); 448 permissions.add(new PropertyPermission("test.boot.only", "read")); 449 permissions.add(new FilePermission("<<ALL FILES>>", "read")); 450 451 // these are used for configuring the test itself... 452 allPermissions = new Permissions(); 453 allPermissions.add(new java.security.AllPermission()); 454 } 455 456 @Override 457 public boolean implies(ProtectionDomain domain, Permission permission) { 458 if (allowAll.get().get()) return allPermissions.implies(permission); 459 if (permissions.implies(permission)) return true; 460 if (permission instanceof java.lang.RuntimePermission) { 461 if (permission.getName().startsWith("accessClassInPackage.")) { 462 // add these along to the set of permission we have, when we 463 // discover that we need them. 464 permissions.add(permission); 465 return true; 466 } 467 } 468 return false; 469 } 470 471 @Override 472 public PermissionCollection getPermissions(CodeSource codesource) { 473 return new PermissionsBuilder().addAll(allowAll.get().get() 474 ? allPermissions : permissions).toPermissions(); 475 } 476 477 @Override 478 public PermissionCollection getPermissions(ProtectionDomain domain) { 479 return new PermissionsBuilder().addAll(allowAll.get().get() 480 ? allPermissions : permissions).toPermissions(); 481 } 482 } 483 484 }