1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2018 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 /**
  27  * @test
  28  * @summary Test messages of IllegalAccessError.
  29  * @modules java.base/java.lang:open
  30  *          java.base/jdk.internal.org.objectweb.asm
  31  * @compile IAE_Loader1.java IAE_Loader2.java IAE78_A.java IAE78_B.java
  32  *          IllegalAccessErrorTest.java
  33  * @run main/othervm -Xbootclasspath/a:. test.IllegalAccessErrorTest
  34  */
  35 
  36 // Put this test into a package so we see qualified class names in
  37 // the error messages. Verify that classes are printed with '.' instead
  38 // of '/'.
  39 package test;
  40 
  41 import java.lang.reflect.*;
  42 import java.lang.invoke.MethodHandles.Lookup;
  43 import static java.lang.invoke.MethodHandles.*;
  44 import static java.lang.invoke.MethodHandles.Lookup.*;
  45 import java.security.*;
  46 
  47 import jdk.internal.org.objectweb.asm.ClassWriter;
  48 import jdk.internal.org.objectweb.asm.MethodVisitor;
  49 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  50 
  51 import test.*;
  52 
  53 abstract public class IllegalAccessErrorTest {
  54 
  55     // interface
  56     private static String expectedErrorMessage1a =
  57         "class test.IAE1_B cannot access its superinterface test.IAE1_A";
  58     private static String expectedErrorMessage1b =
  59         "class someCLName1//test.IAE1_B cannot access its superinterface test.IAE1_A";
  60 
  61     // abstract class
  62     private static String expectedErrorMessage2 =
  63         "class someCLName2//test.IAE2_B cannot access its abstract superclass test.IAE2_A";
  64 
  65     // class
  66     private static String expectedErrorMessage3 =
  67         "class someCLName3//test.IAE3_B cannot access its superclass test.IAE3_A";
  68 
  69     public static void test123(String loaderName,
  70                                String expectedErrorMessage,
  71                                String testClass) throws Exception {
  72         String[] classNames = { testClass };
  73         // Some classes under a new Loader.
  74         ClassLoader l = new IAE_Loader1(loaderName, classNames);
  75 
  76         try {
  77             l.loadClass(testClass);
  78             throw new RuntimeException("Expected IllegalAccessError was not thrown.");
  79         } catch (IllegalAccessError iae) {
  80             String errorMsg = iae.getMessage();
  81             if (!errorMsg.equals(expectedErrorMessage)) {
  82                 System.out.println("Expected: " + expectedErrorMessage + "\n" +
  83                                    "but got:  " + errorMsg);
  84                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
  85             } else {
  86                 System.out.println("Passed with message: " + errorMsg);
  87             }
  88         }
  89     }
  90 
  91     // Generate a class file with the given class name. The class implements Runnable
  92     // with a run method to invokestatic the given targetClass/targetMethod.
  93     static byte[] iae4_generateRunner(String className,
  94                                       String targetClass,
  95                                       String targetMethod) throws Exception {
  96 
  97         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
  98                                          + ClassWriter.COMPUTE_FRAMES);
  99         cw.visit(V9,
 100                 ACC_PUBLIC + ACC_SUPER,
 101                 className.replace(".", "/"),
 102                 null,
 103                 "java/lang/Object",
 104                 new String[] { "java/lang/Runnable" });
 105 
 106         // <init>
 107         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 108         mv.visitVarInsn(ALOAD, 0);
 109         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 110         mv.visitInsn(RETURN);
 111         mv.visitMaxs(0, 0);
 112         mv.visitEnd();
 113 
 114         // run()
 115         String tc = targetClass.replace(".", "/");
 116         mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
 117         mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
 118         mv.visitInsn(RETURN);
 119         mv.visitMaxs(0, 0);
 120         mv.visitEnd();
 121 
 122         cw.visitEnd();
 123         return cw.toByteArray();
 124     }
 125 
 126     // Private method that should raise IllegalAccessError when called.
 127     private static void iae4_m() { }
 128 
 129     private static String expectedErrorMessage4 =
 130          "tried to access private method test.IllegalAccessErrorTest.iae4_m()V from class test.Runner4";
 131 
 132     // Test according to java/lang/invoke/DefineClassTest.java
 133     public static void test4_privateMethod() throws Exception {
 134         final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName();
 135         final String THIS_CLASS   = IllegalAccessErrorTest.class.getName();
 136         final String CLASS_NAME   = THIS_PACKAGE + ".Runner4";
 137         Lookup lookup = lookup();
 138 
 139         // private
 140         byte[] classBytes = iae4_generateRunner(CLASS_NAME, THIS_CLASS, "iae4_m");
 141         Class<?> clazz = lookup.defineClass(classBytes);
 142         Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance();
 143         try {
 144             r.run();
 145             throw new RuntimeException("Expected IllegalAccessError was not thrown.");
 146         } catch (IllegalAccessError exc) {
 147             String errorMsg = exc.getMessage();
 148             if (!errorMsg.equals(expectedErrorMessage4)) {
 149                 System.out.println("Expected: " + expectedErrorMessage4 + "\n" +
 150                                    "but got:  " + errorMsg);
 151                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
 152             }
 153             System.out.println("Passed with message: " + errorMsg);
 154         }
 155     }
 156 
 157     // Generate a class file with the given class name. The class implements Runnable
 158     // with a run method to invokestatic the given targetClass/targetField.
 159     static byte[] iae5_generateRunner(String className,
 160                                       String targetClass,
 161                                       String targetField) throws Exception {
 162 
 163         ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
 164                                          + ClassWriter.COMPUTE_FRAMES);
 165         cw.visit(V9,
 166                  ACC_PUBLIC + ACC_SUPER,
 167                  className.replace(".", "/"),
 168                  null,
 169                  "java/lang/Object",
 170                  new String[] { "java/lang/Runnable" });
 171 
 172         // <init>
 173         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
 174         mv.visitVarInsn(ALOAD, 0);
 175         mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
 176         mv.visitInsn(RETURN);
 177         mv.visitMaxs(0, 0);
 178         mv.visitEnd();
 179 
 180         // run()
 181         String tc = targetClass.replace(".", "/");
 182         mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
 183         mv.visitFieldInsn(GETSTATIC, tc, targetField, "I");
 184         mv.visitInsn(RETURN);
 185         mv.visitMaxs(0, 0);
 186         mv.visitEnd();
 187 
 188         cw.visitEnd();
 189         return cw.toByteArray();
 190     }
 191 
 192     // Private field that should raise IllegalAccessError when accessed.
 193     private static int iae5_f = 77;
 194 
 195     private static String expectedErrorMessage5 =
 196         "tried to access private field test.IllegalAccessErrorTest.iae5_f from class test.Runner5";
 197 
 198     // Test according to java/lang/invoke/DefineClassTest.java
 199     public static void test5_privateField() throws Exception {
 200         final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName();
 201         final String THIS_CLASS   = IllegalAccessErrorTest.class.getName();
 202         final String CLASS_NAME   = THIS_PACKAGE + ".Runner5";
 203         Lookup lookup = lookup();
 204 
 205         // private
 206         byte[] classBytes = iae5_generateRunner(CLASS_NAME, THIS_CLASS, "iae5_f");
 207         Class<?> clazz = lookup.defineClass(classBytes);
 208         Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance();
 209         try {
 210             r.run();
 211             throw new RuntimeException("Expected IllegalAccessError was not thrown.");
 212         } catch (IllegalAccessError exc) {
 213             String errorMsg = exc.getMessage();
 214             if (!errorMsg.equals(expectedErrorMessage5)) {
 215                 System.out.println("Expected: " + expectedErrorMessage5 + "\n" +
 216                                    "but got:  " + errorMsg);
 217                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
 218             }
 219             System.out.println("Passed with message: " + errorMsg);
 220         }
 221     }
 222 
 223     private static String expectedErrorMessage6 =
 224         "failed to access class test.IAE6_A from class test6_class_CL//test.IAE6_B";
 225 
 226     public static void test6_class() throws Exception {
 227         ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
 228         IAE_Loader2 loader = new IAE_Loader2("test6_class_CL", base.getParent(), base, new String[0],
 229                 new String[] { IAE6_A.class.getName() });
 230         Class<?> cl = loader.loadClass(IAE6_B.class.getName());
 231         Method m = cl.getDeclaredMethod("create", new Class[0]);
 232         m.setAccessible(true);
 233 
 234         try {
 235             m.invoke(null, new Object[0]);
 236             throw new RuntimeException("Expected IllegalAccessError was not thrown.");
 237         } catch (InvocationTargetException e) {
 238             IllegalAccessError iae = (IllegalAccessError) e.getCause();
 239             String errorMsg = iae.getMessage();
 240             if (!errorMsg.equals(expectedErrorMessage6)) {
 241                 System.out.println("Expected: " + expectedErrorMessage6 + "\n" +
 242                                    "but got:  " + errorMsg);
 243                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
 244             }
 245             System.out.println("Passed with message: " + errorMsg);
 246         }
 247     }
 248 
 249     private static String expectedErrorMessage7 =
 250         "tried to access method test.IAE78_A.<init>()V from class test7_method_CL//test.IAE78_B";
 251 
 252     // Similar to test4.
 253     public static void test7_method() throws Exception {
 254         ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
 255         IAE_Loader2 loader = new IAE_Loader2("test7_method_CL", base.getParent(), base, new String[0],
 256                 new String[] {IAE78_A.class.getName()});
 257         Class<?> cl = loader.loadClass(IAE78_B.class.getName());
 258         Method m = cl.getDeclaredMethod("create", new Class[0]);
 259 
 260         try {
 261             m.invoke(null, new Object[0]);
 262         } catch (InvocationTargetException e) {
 263             IllegalAccessError iae = (IllegalAccessError) e.getCause();
 264             String errorMsg = iae.getMessage();
 265             if (!errorMsg.equals(expectedErrorMessage7)) {
 266                 System.out.println("Expected: " + expectedErrorMessage7 + "\n" +
 267                                    "but got:  " + errorMsg);
 268                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
 269             }
 270             System.out.println("Passed with message: " + errorMsg);
 271         }
 272     }
 273 
 274     private static String expectedErrorMessage8 =
 275         "tried to access field test.IAE78_A.f from class test8_field_CL//test.IAE78_B";
 276 
 277     // Similar to test5.
 278     public static void test8_field() throws Exception {
 279         ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
 280         IAE_Loader2 loader = new IAE_Loader2("test8_field_CL", base.getParent(), base, new String[0],
 281                                              new String[] { IAE78_A.class.getName() });
 282         Class<?> cl = loader.loadClass(IAE78_B.class.getName());
 283         Method m = cl.getDeclaredMethod("access", new Class[0]);
 284 
 285         try {
 286             m.invoke(null, new Object[0]);
 287         }
 288         catch (InvocationTargetException e) {
 289             IllegalAccessError iae = (IllegalAccessError) e.getCause();
 290             String errorMsg = iae.getMessage();
 291             if (!errorMsg.equals(expectedErrorMessage8)) {
 292                 System.out.println("Expected: " + expectedErrorMessage8 + "\n" +
 293                                    "but got:  " + errorMsg);
 294                 throw new RuntimeException("Wrong error message of IllegalAccessError.");
 295             }
 296             System.out.println("Passed with message: " + errorMsg);
 297         }
 298     }
 299 
 300     public static void main(String[] args) throws Exception {
 301         test123(null,          expectedErrorMessage1a, "test.IAE1_B"); // interface
 302         test123("someCLName1", expectedErrorMessage1b, "test.IAE1_B"); // interface
 303         test123("someCLName2", expectedErrorMessage2,  "test.IAE2_B"); // abstract class
 304         test123("someCLName3", expectedErrorMessage3,  "test.IAE3_B"); // class
 305         test4_privateMethod();
 306         test5_privateField();
 307         test6_class();
 308         test7_method();
 309         test8_field();
 310     }
 311 }
 312 
 313 // Class hierarchies for test1.
 314 interface IAE1_A {
 315     public IAE1_D gen();
 316 }
 317 
 318 class IAE1_B implements IAE1_A {
 319     public IAE1_D gen() {
 320         return null;
 321     }
 322 }
 323 
 324 abstract class IAE1_C {
 325 }
 326 
 327 class IAE1_D extends IAE1_C {
 328 }
 329 
 330 
 331 // Class hierarchies for test2.
 332 abstract class IAE2_A {
 333     abstract public IAE2_D gen();
 334 }
 335 
 336 class IAE2_B extends IAE2_A {
 337     public IAE2_D gen() {
 338         return null;
 339     }
 340 }
 341 
 342 abstract class IAE2_C {
 343 }
 344 
 345 class IAE2_D extends IAE2_C {
 346 }
 347 
 348 
 349 // Class hierarchies for test3.
 350 class IAE3_A {
 351     public IAE3_D gen() {
 352         return null;
 353     };
 354 }
 355 
 356 class IAE3_B extends IAE3_A {
 357     public IAE3_D gen() {
 358         return null;
 359     }
 360 }
 361 
 362 abstract class IAE3_C {
 363 }
 364 
 365 class IAE3_D extends IAE3_C {
 366 }
 367 
 368 
 369 // Class hierarchies for test6.
 370 class IAE6_A {
 371     IAE6_A() {
 372         // Nothing to do.
 373     }
 374 }
 375 
 376 class IAE6_B {
 377     public static void create() {
 378         new IAE6_A();
 379     }
 380 }