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