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