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.FilePermission; 25 import java.io.IOException; 26 import java.lang.reflect.AccessibleObject; 27 import java.lang.reflect.Field; 28 import java.lang.reflect.Module; 29 import java.lang.reflect.Modifier; 30 import java.lang.reflect.InaccessibleObjectException; 31 import java.lang.reflect.ReflectPermission; 32 import java.net.URI; 33 import java.nio.file.FileSystem; 34 import java.nio.file.FileSystems; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.security.CodeSource; 38 import java.security.Permission; 39 import java.security.PermissionCollection; 40 import java.security.Permissions; 41 import java.security.Policy; 42 import java.security.ProtectionDomain; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.Enumeration; 47 import java.util.Iterator; 48 import java.util.List; 49 import java.util.PropertyPermission; 50 import java.util.concurrent.atomic.AtomicBoolean; 51 import java.util.concurrent.atomic.AtomicLong; 52 import java.util.stream.Stream; 53 54 import jdk.internal.module.Modules; 55 56 /** 57 * @test 58 * @bug 8065552 59 * @summary test that all fields returned by getDeclaredFields() can be 60 * set accessible if the right permission is granted; this test 61 * loads all the classes in the BCL, get their declared fields, 62 * and call setAccessible(false) followed by setAccessible(true); 63 * @modules java.base/jdk.internal.module 64 * @run main/othervm FieldSetAccessibleTest UNSECURE 65 * @run main/othervm FieldSetAccessibleTest SECURE 66 * 67 * @author danielfuchs 68 */ 69 public class FieldSetAccessibleTest { 70 71 static final List<String> skipped = new ArrayList<>(); 72 static final List<String> cantread = new ArrayList<>(); 73 static final List<String> failed = new ArrayList<>(); 74 static final AtomicLong classCount = new AtomicLong(); 75 static final AtomicLong fieldCount = new AtomicLong(); 76 static long startIndex = 0; 77 static long maxSize = Long.MAX_VALUE; 78 static long maxIndex = Long.MAX_VALUE; 79 static final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); 80 81 82 // Test that all fields for any given class can be made accessibles 83 static void testSetFieldsAccessible(Class<?> c, boolean expectException) { 84 for (Field f : c.getDeclaredFields()) { 85 fieldCount.incrementAndGet(); 86 boolean expect = expectException; 87 if ((c == Module.class || c == AccessibleObject.class) && 88 !Modifier.isPublic(f.getModifiers())) { 89 expect = true; 90 } 91 try { 92 f.setAccessible(false); 93 f.setAccessible(true); 94 if (expect) { 95 throw new RuntimeException( 96 String.format("Expected InaccessibleObjectException is not thrown " 97 + "for field %s in class %s%n", f.getName(), c.getName())); 98 } 99 } catch (InaccessibleObjectException expected) { 100 if (!expect) { 101 throw new RuntimeException(expected); 102 } 103 } 104 } 105 } 106 107 // Performs a series of test on the given class. 108 // At this time, we only call testSetFieldsAccessible(c) 109 public static boolean test(Class<?> c, boolean addExports) { 110 Module self = FieldSetAccessibleTest.class.getModule(); 111 Module target = c.getModule(); 112 String pn = c.getPackage().getName(); 113 boolean exported = self.canRead(target) && target.isExported(pn, self); 114 if (addExports && !exported) { 115 Modules.addExports(target, pn, self); 116 exported = true; 117 } 118 boolean expectException = !exported; 119 120 classCount.incrementAndGet(); 121 122 // Call getDeclaredFields() and try to set their accessible flag. 123 testSetFieldsAccessible(c, expectException); 124 125 // add more tests here... 126 127 return c == Class.class; 128 } 129 130 // Prints a summary at the end of the test. 131 static void printSummary(long secs, long millis, long nanos) { 132 System.out.println("Tested " + fieldCount.get() + " fields of " 133 + classCount.get() + " classes in " 134 + secs + "s " + millis + "ms " + nanos + "ns"); 135 } 136 137 138 /** 139 * @param args the command line arguments: 140 * 141 * SECURE|UNSECURE [startIndex (default=0)] [maxSize (default=Long.MAX_VALUE)] 142 * 143 * @throws java.lang.Exception if the test fails 144 */ 145 public static void main(String[] args) throws Exception { 146 if (args == null || args.length == 0) { 147 args = new String[] {"SECURE", "0"}; 148 } else if (args.length > 3) { 149 throw new RuntimeException("Expected at most one argument. Found " 150 + Arrays.asList(args)); 151 } 152 try { 153 if (args.length > 1) { 154 startIndex = Long.parseLong(args[1]); 155 if (startIndex < 0) { 156 throw new IllegalArgumentException("startIndex args[1]: " 157 + startIndex); 158 } 159 } 160 if (args.length > 2) { 161 maxSize = Long.parseLong(args[2]); 162 if (maxSize <= 0) { 163 maxSize = Long.MAX_VALUE; 164 } 165 maxIndex = (Long.MAX_VALUE - startIndex) < maxSize 166 ? Long.MAX_VALUE : startIndex + maxSize; 167 } 168 TestCase.valueOf(args[0]).run(); 169 } catch (OutOfMemoryError oome) { 170 System.err.println(classCount.get()); 171 throw oome; 172 } 173 } 174 175 public static void run(TestCase test) { 176 System.out.println("Testing " + test); 177 test(listAllClassNames()); 178 System.out.println("Passed " + test); 179 } 180 181 static Iterable<String> listAllClassNames() { 182 return new ClassNameJrtStreamBuilder(); 183 } 184 185 static void test(Iterable<String> iterable) { 186 final long start = System.nanoTime(); 187 boolean classFound = false; 188 int index = 0; 189 for (String s : iterable) { 190 if (index == maxIndex) break; 191 try { 192 if (index < startIndex) continue; 193 if (test(s, false)) { 194 classFound = true; 195 } 196 } finally { 197 index++; 198 } 199 } 200 201 // Re-test with all packages exported 202 for (String s : iterable) { 203 test(s, true); 204 } 205 206 classCount.set(classCount.get() / 2); 207 fieldCount.set(fieldCount.get() / 2); 208 long elapsed = System.nanoTime() - start; 209 long secs = elapsed / 1000_000_000; 210 long millis = (elapsed % 1000_000_000) / 1000_000; 211 long nanos = elapsed % 1000_000; 212 System.out.println("Unreadable path elements: " + cantread); 213 System.out.println("Skipped path elements: " + skipped); 214 System.out.println("Failed path elements: " + failed); 215 printSummary(secs, millis, nanos); 216 217 if (!failed.isEmpty()) { 218 throw new RuntimeException("Test failed for the following classes: " + failed); 219 } 220 if (!classFound && startIndex == 0 && index < maxIndex) { 221 // this is just to verify that we have indeed parsed rt.jar 222 // (or the java.base module) 223 throw new RuntimeException("Test failed: Class.class not found..."); 224 } 225 if (classCount.get() == 0 && startIndex == 0) { 226 throw new RuntimeException("Test failed: no class found?"); 227 } 228 } 229 230 static boolean test(String s, boolean addExports) { 231 try { 232 final Class<?> c = Class.forName( 233 s.replace('/', '.').substring(0, s.length() - 6), 234 false, 235 systemClassLoader); 236 return test(c, addExports); 237 } catch (Exception t) { 238 t.printStackTrace(System.err); 239 failed.add(s); 240 } catch (NoClassDefFoundError e) { 241 e.printStackTrace(System.err); 242 failed.add(s); 243 } 244 return false; 245 } 246 247 static class ClassNameJrtStreamBuilder implements Iterable<String>{ 248 249 final FileSystem jrt; 250 final List<Path> roots = new ArrayList<>(); 251 ClassNameJrtStreamBuilder() { 252 jrt = FileSystems.getFileSystem(URI.create("jrt:/")); 253 for (Path root : jrt.getRootDirectories()) { 254 roots.add(root); 255 } 256 } 257 258 Stream<String> build() { 259 return roots.stream().flatMap(this::toStream) 260 .filter(x -> x.getNameCount() > 2) 261 .map( x-> x.subpath(2, x.getNameCount())) 262 .map( x -> x.toString()) 263 .filter(s -> s.endsWith(".class") && !s.endsWith("module-info.class")); 264 } 265 266 @Override 267 public Iterator<String> iterator() { 268 return build().iterator(); 269 } 270 271 private Stream<Path> toStream(Path root) { 272 try { 273 return Files.walk(root); 274 } catch(IOException x) { 275 x.printStackTrace(System.err); 276 skipped.add(root.toString()); 277 } 278 return Collections.<Path>emptyList().stream(); 279 } 280 281 } 282 283 // Test with or without a security manager 284 public static enum TestCase { 285 UNSECURE, SECURE; 286 public void run() throws Exception { 287 System.out.println("Running test case: " + name()); 288 Configure.setUp(this); 289 FieldSetAccessibleTest.run(this); 290 } 291 } 292 293 // A helper class to configure the security manager for the test, 294 // and bypass it when needed. 295 static class Configure { 296 static Policy policy = null; 297 static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() { 298 @Override 299 protected AtomicBoolean initialValue() { 300 return new AtomicBoolean(false); 301 } 302 }; 303 static void setUp(TestCase test) { 304 switch (test) { 305 case SECURE: 306 if (policy == null && System.getSecurityManager() != null) { 307 throw new IllegalStateException("SecurityManager already set"); 308 } else if (policy == null) { 309 policy = new SimplePolicy(TestCase.SECURE, allowAll); 310 Policy.setPolicy(policy); 311 System.setSecurityManager(new SecurityManager()); 312 } 313 if (System.getSecurityManager() == null) { 314 throw new IllegalStateException("No SecurityManager."); 315 } 316 if (policy == null) { 317 throw new IllegalStateException("policy not configured"); 318 } 319 break; 320 case UNSECURE: 321 if (System.getSecurityManager() != null) { 322 throw new IllegalStateException("SecurityManager already set"); 323 } 324 break; 325 default: 326 throw new InternalError("No such testcase: " + test); 327 } 328 } 329 static void doPrivileged(Runnable run) { 330 allowAll.get().set(true); 331 try { 332 run.run(); 333 } finally { 334 allowAll.get().set(false); 335 } 336 } 337 } 338 339 // A Helper class to build a set of permissions. 340 static final class PermissionsBuilder { 341 final Permissions perms; 342 public PermissionsBuilder() { 343 this(new Permissions()); 344 } 345 public PermissionsBuilder(Permissions perms) { 346 this.perms = perms; 347 } 348 public PermissionsBuilder add(Permission p) { 349 perms.add(p); 350 return this; 351 } 352 public PermissionsBuilder addAll(PermissionCollection col) { 353 if (col != null) { 354 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) { 355 perms.add(e.nextElement()); 356 } 357 } 358 return this; 359 } 360 public Permissions toPermissions() { 361 final PermissionsBuilder builder = new PermissionsBuilder(); 362 builder.addAll(perms); 363 return builder.perms; 364 } 365 } 366 367 // Policy for the test... 368 public static class SimplePolicy extends Policy { 369 370 final Permissions permissions; 371 final Permissions allPermissions; 372 final ThreadLocal<AtomicBoolean> allowAll; 373 public SimplePolicy(TestCase test, ThreadLocal<AtomicBoolean> allowAll) { 374 this.allowAll = allowAll; 375 376 // Permission needed by the tested code exercised in the test 377 permissions = new Permissions(); 378 permissions.add(new RuntimePermission("fileSystemProvider")); 379 permissions.add(new RuntimePermission("createClassLoader")); 380 permissions.add(new RuntimePermission("closeClassLoader")); 381 permissions.add(new RuntimePermission("getClassLoader")); 382 permissions.add(new RuntimePermission("accessDeclaredMembers")); 383 permissions.add(new ReflectPermission("suppressAccessChecks")); 384 permissions.add(new PropertyPermission("*", "read")); 385 permissions.add(new FilePermission("<<ALL FILES>>", "read")); 386 387 // these are used for configuring the test itself... 388 allPermissions = new Permissions(); 389 allPermissions.add(new java.security.AllPermission()); 390 } 391 392 @Override 393 public boolean implies(ProtectionDomain domain, Permission permission) { 394 if (allowAll.get().get()) return allPermissions.implies(permission); 395 if (permissions.implies(permission)) return true; 396 if (permission instanceof java.lang.RuntimePermission) { 397 if (permission.getName().startsWith("accessClassInPackage.")) { 398 // add these along to the set of permission we have, when we 399 // discover that we need them. 400 permissions.add(permission); 401 return true; 402 } 403 } 404 return false; 405 } 406 407 @Override 408 public PermissionCollection getPermissions(CodeSource codesource) { 409 return new PermissionsBuilder().addAll(allowAll.get().get() 410 ? allPermissions : permissions).toPermissions(); 411 } 412 413 @Override 414 public PermissionCollection getPermissions(ProtectionDomain domain) { 415 return new PermissionsBuilder().addAll(allowAll.get().get() 416 ? allPermissions : permissions).toPermissions(); 417 } 418 } 419 420 }