changeset 11945:274dc4b09989

8058575: IllegalAccessError trying to access package-private class from VM anonymous class Summary: Put anonymous classes in unnamed package into host class's package. Throw exception if host class's package differs from anonymous class. Reviewed-by: coleenp, acorn
author hseigel
date Wed, 07 Sep 2016 07:19:48 -0400
parents 6d3c44100184
children d3a49c3aa08f
files src/share/vm/classfile/classFileParser.cpp src/share/vm/classfile/classFileParser.hpp src/share/vm/classfile/klassFactory.cpp src/share/vm/classfile/klassFactory.hpp src/share/vm/classfile/systemDictionary.cpp src/share/vm/classfile/systemDictionary.hpp src/share/vm/classfile/verifier.cpp src/share/vm/oops/instanceKlass.hpp src/share/vm/prims/unsafe.cpp src/share/vm/runtime/reflection.cpp test/compiler/jsr292/CallSiteDepContextTest.java test/runtime/defineAnonClass/DefineAnon.java test/runtime/defineAnonClass/NestedUnsafe.java test/runtime/defineAnonClass/NestedUnsafe2.java
diffstat 14 files changed, 416 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/classfile/classFileParser.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/classFileParser.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -5407,6 +5407,59 @@
   debug_only(ik->verify();)
 }
 
+// For an anonymous class that is in the unnamed package, move it to its host class's
+// package by prepending its host class's package name to its class name and setting
+// its _class_name field.
+void ClassFileParser::prepend_host_package_name(const InstanceKlass* host_klass, TRAPS) {
+  ResourceMark rm(THREAD);
+  assert(strrchr(_class_name->as_C_string(), '/') == NULL,
+         "Anonymous class should not be in a package");
+  const char* host_pkg_name =
+    ClassLoader::package_from_name(host_klass->name()->as_C_string(), NULL);
+
+  if (host_pkg_name != NULL) {
+    size_t host_pkg_len = strlen(host_pkg_name);
+    int class_name_len = _class_name->utf8_length();
+    char* new_anon_name =
+      NEW_RESOURCE_ARRAY(char, host_pkg_len + 1 + class_name_len);
+    // Copy host package name and trailing /.
+    strncpy(new_anon_name, host_pkg_name, host_pkg_len);
+    new_anon_name[host_pkg_len] = '/';
+    // Append anonymous class name. The anonymous class name can contain odd
+    // characters.  So, do a strncpy instead of using sprintf("%s...").
+    strncpy(new_anon_name + host_pkg_len + 1, (char *)_class_name->base(), class_name_len);
+
+    // Create a symbol and update the anonymous class name.
+    _class_name = SymbolTable::new_symbol(new_anon_name,
+                                          (int)host_pkg_len + 1 + class_name_len,
+                                          CHECK);
+  }
+}
+
+// If the host class and the anonymous class are in the same package then do
+// nothing.  If the anonymous class is in the unnamed package then move it to its
+// host's package.  If the classes are in different packages then throw an IAE
+// exception.
+void ClassFileParser::fix_anonymous_class_name(TRAPS) {
+  assert(_host_klass != NULL, "Expected an anonymous class");
+
+  const jbyte* anon_last_slash = UTF8::strrchr(_class_name->base(),
+                                               _class_name->utf8_length(), '/');
+  if (anon_last_slash == NULL) {  // Unnamed package
+    prepend_host_package_name(_host_klass, CHECK);
+  } else {
+    if (!InstanceKlass::is_same_class_package(_host_klass->class_loader(),
+                                              _host_klass->name(),
+                                              _host_klass->class_loader(),
+                                              _class_name)) {
+      ResourceMark rm(THREAD);
+      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
+        err_msg("Host class %s and anonymous class %s are in different packages",
+        _host_klass->name()->as_C_string(), _class_name->as_C_string()));
+    }
+  }
+}
+
 static bool relax_format_check_for(ClassLoaderData* loader_data) {
   bool trusted = (loader_data->is_the_null_class_loader_data() ||
                   SystemDictionary::is_platform_class_loader(loader_data->class_loader()));
@@ -5422,7 +5475,7 @@
                                  Symbol* name,
                                  ClassLoaderData* loader_data,
                                  Handle protection_domain,
-                                 const Klass* host_klass,
+                                 const InstanceKlass* host_klass,
                                  GrowableArray<Handle>* cp_patches,
                                  Publicity pub_level,
                                  TRAPS) :
@@ -5697,6 +5750,13 @@
     return;
   }
 
+  // if this is an anonymous class fix up its name if it's in the unnamed
+  // package.  Otherwise, throw IAE if it is in a different package than
+  // its host class.
+  if (_host_klass != NULL) {
+    fix_anonymous_class_name(CHECK);
+  }
+
   // Verification prevents us from creating names with dots in them, this
   // asserts that that's the case.
   assert(is_internal_format(_class_name), "external class name format used internally");
--- a/src/share/vm/classfile/classFileParser.hpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/classFileParser.hpp	Wed Sep 07 07:19:48 2016 -0400
@@ -79,7 +79,7 @@
   const Symbol* _requested_name;
   Symbol* _class_name;
   mutable ClassLoaderData* _loader_data;
-  const Klass* _host_klass;
+  const InstanceKlass* _host_klass;
   GrowableArray<Handle>* _cp_patches; // overrides for CP entries
 
   // Metadata created before the instance klass is created.  Must be deallocated
@@ -155,6 +155,9 @@
                                   ConstantPool* cp,
                                   TRAPS);
 
+  void prepend_host_package_name(const InstanceKlass* host_klass, TRAPS);
+  void fix_anonymous_class_name(TRAPS);
+
   void fill_instance_klass(InstanceKlass* ik, bool cf_changed_in_CFLH, TRAPS);
   void set_klass(InstanceKlass* instance);
 
@@ -474,7 +477,7 @@
                   Symbol* name,
                   ClassLoaderData* loader_data,
                   Handle protection_domain,
-                  const Klass* host_klass,
+                  const InstanceKlass* host_klass,
                   GrowableArray<Handle>* cp_patches,
                   Publicity pub_level,
                   TRAPS);
@@ -500,7 +503,7 @@
   bool is_anonymous() const { return _host_klass != NULL; }
   bool is_interface() const { return _access_flags.is_interface(); }
 
-  const Klass* host_klass() const { return _host_klass; }
+  const InstanceKlass* host_klass() const { return _host_klass; }
   const GrowableArray<Handle>* cp_patches() const { return _cp_patches; }
   ClassLoaderData* loader_data() const { return _loader_data; }
   const Symbol* class_name() const { return _class_name; }
--- a/src/share/vm/classfile/klassFactory.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/klassFactory.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -94,7 +94,7 @@
                                                      Symbol* name,
                                                      ClassLoaderData* loader_data,
                                                      Handle protection_domain,
-                                                     const Klass* host_klass,
+                                                     const InstanceKlass* host_klass,
                                                      GrowableArray<Handle>* cp_patches,
                                                      TRAPS) {
 
--- a/src/share/vm/classfile/klassFactory.hpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/klassFactory.hpp	Wed Sep 07 07:19:48 2016 -0400
@@ -72,7 +72,7 @@
                                                 Symbol* name,
                                                 ClassLoaderData* loader_data,
                                                 Handle protection_domain,
-                                                const Klass* host_klass,
+                                                const InstanceKlass* host_klass,
                                                 GrowableArray<Handle>* cp_patches,
                                                 TRAPS);
 };
--- a/src/share/vm/classfile/systemDictionary.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/systemDictionary.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -1027,7 +1027,7 @@
                                       Handle class_loader,
                                       Handle protection_domain,
                                       ClassFileStream* st,
-                                      const Klass* host_klass,
+                                      const InstanceKlass* host_klass,
                                       GrowableArray<Handle>* cp_patches,
                                       TRAPS) {
 
--- a/src/share/vm/classfile/systemDictionary.hpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/systemDictionary.hpp	Wed Sep 07 07:19:48 2016 -0400
@@ -299,7 +299,7 @@
                              Handle class_loader,
                              Handle protection_domain,
                              ClassFileStream* st,
-                             const Klass* host_klass,
+                             const InstanceKlass* host_klass,
                              GrowableArray<Handle>* cp_patches,
                              TRAPS);
 
--- a/src/share/vm/classfile/verifier.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/classfile/verifier.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -2786,7 +2786,7 @@
       // direct interface relative to the host class
       have_imr_indirect = (have_imr_indirect &&
                            !is_same_or_direct_interface(
-                             InstanceKlass::cast(current_class()->host_klass()),
+                             current_class()->host_klass(),
                              host_klass_type, ref_class_type));
     }
     if (!subtype) {
--- a/src/share/vm/oops/instanceKlass.hpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/oops/instanceKlass.hpp	Wed Sep 07 07:19:48 2016 -0400
@@ -619,8 +619,8 @@
   objArrayOop signers() const;
 
   // host class
-  Klass* host_klass() const              {
-    Klass** hk = (Klass**)adr_host_klass();
+  InstanceKlass* host_klass() const              {
+    InstanceKlass** hk = adr_host_klass();
     if (hk == NULL) {
       return NULL;
     } else {
@@ -628,9 +628,9 @@
       return *hk;
     }
   }
-  void set_host_klass(const Klass* host) {
+  void set_host_klass(const InstanceKlass* host) {
     assert(is_anonymous(), "not anonymous");
-    const Klass** addr = (const Klass**)adr_host_klass();
+    const InstanceKlass** addr = (const InstanceKlass **)adr_host_klass();
     assert(addr != NULL, "no reversed space");
     if (addr != NULL) {
       *addr = host;
@@ -1057,13 +1057,13 @@
     }
   };
 
-  Klass** adr_host_klass() const {
+  InstanceKlass** adr_host_klass() const {
     if (is_anonymous()) {
-      Klass** adr_impl = adr_implementor();
+      InstanceKlass** adr_impl = (InstanceKlass **)adr_implementor();
       if (adr_impl != NULL) {
         return adr_impl + 1;
       } else {
-        return end_of_nonstatic_oop_maps();
+        return (InstanceKlass **)end_of_nonstatic_oop_maps();
       }
     } else {
       return NULL;
--- a/src/share/vm/prims/unsafe.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/prims/unsafe.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -783,6 +783,7 @@
 
 // define a class but do not make it known to the class loader or system dictionary
 // - host_class:  supplies context for linkage, access control, protection domain, and class loader
+//                if host_class is itself anonymous then it is replaced with its host class.
 // - data:  bytes of a class file, a raw memory address (length gives the number of bytes)
 // - cp_patches:  where non-null entries exist, they replace corresponding CP entries in data
 
@@ -791,8 +792,12 @@
 // link to any member of U.  Just after U is loaded, the only way to use it is reflectively,
 // through java.lang.Class methods like Class.newInstance.
 
+// The package of an anonymous class must either match its host's class's package or be in the
+// unnamed package.  If it is in the unnamed package then it will be put in its host class's
+// package.
+//
+
 // Access checks for linkage sites within U continue to follow the same rules as for named classes.
-// The package of an anonymous class is given by the package qualifier on the name under which it was loaded.
 // An anonymous class also has special privileges to access any member of its host class.
 // This is the main reason why this loading operation is unsafe.  The purpose of this is to
 // allow language implementations to simulate "open classes"; a host class in effect gets
@@ -874,9 +879,11 @@
 
   // Primitive types have NULL Klass* fields in their java.lang.Class instances.
   if (host_klass == NULL) {
-    THROW_0(vmSymbols::java_lang_IllegalArgumentException());
+    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Host class is null");
   }
 
+  assert(host_klass->is_instance_klass(), "Host class must be an instance class");
+
   const char* host_source = host_klass->external_name();
   Handle      host_loader(THREAD, host_klass->class_loader());
   Handle      host_domain(THREAD, host_klass->protection_domain());
@@ -907,7 +914,7 @@
                                                 host_loader,
                                                 host_domain,
                                                 &st,
-                                                host_klass,
+                                                InstanceKlass::cast(host_klass),
                                                 cp_patches,
                                                 CHECK_NULL);
   if (anonk == NULL) {
--- a/src/share/vm/runtime/reflection.cpp	Wed Sep 07 09:20:10 2016 +0200
+++ b/src/share/vm/runtime/reflection.cpp	Wed Sep 07 07:19:48 2016 -0400
@@ -412,13 +412,13 @@
   return result;
 }
 
-static bool under_host_klass(const InstanceKlass* ik, const Klass* host_klass) {
+static bool under_host_klass(const InstanceKlass* ik, const InstanceKlass* host_klass) {
   DEBUG_ONLY(int inf_loop_check = 1000 * 1000 * 1000);
   for (;;) {
-    const Klass* hc = (const Klass*)ik->host_klass();
+    const InstanceKlass* hc = ik->host_klass();
     if (hc == NULL)        return false;
     if (hc == host_klass)  return true;
-    ik = InstanceKlass::cast(hc);
+    ik = hc;
 
     // There's no way to make a host class loop short of patching memory.
     // Therefore there cannot be a loop here unless there's another bug.
@@ -436,8 +436,8 @@
 
   // If either is on the other's host_klass chain, access is OK,
   // because one is inside the other.
-  if (under_host_klass(accessor_ik, accessee) ||
-    under_host_klass(accessee_ik, accessor))
+  if (under_host_klass(accessor_ik, accessee_ik) ||
+    under_host_klass(accessee_ik, accessor_ik))
     return true;
 
   if ((RelaxAccessControlCheck &&
--- a/test/compiler/jsr292/CallSiteDepContextTest.java	Wed Sep 07 09:20:10 2016 +0200
+++ b/test/compiler/jsr292/CallSiteDepContextTest.java	Wed Sep 07 07:19:48 2016 -0400
@@ -62,7 +62,7 @@
 public class CallSiteDepContextTest {
     static final Unsafe               UNSAFE = Unsafe.getUnsafe();
     static final MethodHandles.Lookup LOOKUP = MethodHandleHelper.IMPL_LOOKUP;
-    static final String           CLASS_NAME = "java/lang/invoke/Test";
+    static final String           CLASS_NAME = "compiler/jsr292/Test";
     static final String          METHOD_NAME = "m";
     static final MethodType TYPE = MethodType.methodType(int.class);
 
@@ -129,8 +129,8 @@
     }
 
     public static void testSharedCallSite() throws Throwable {
-        Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
-        Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
+        Class<?> cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_1"), null);
+        Class<?> cls2 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_2"), null);
 
         MethodHandle[] mhs = new MethodHandle[] {
                 LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
@@ -151,7 +151,7 @@
         execute(1, mh);
 
         // mcs.context == cls1
-        Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null);
+        Class<?> cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("NonBound_1"), null);
         MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
 
         execute(1, mh1);
@@ -170,8 +170,8 @@
         mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
 
         Class<?>[] cls = new Class[] {
-                UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1" + id), null),
-                UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2" + id), null),
+                UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_1" + id), null),
+                UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_2" + id), null),
         };
 
         MethodHandle[] mhs = new MethodHandle[] {
@@ -185,7 +185,7 @@
         execute(1, mhs);
 
         ref = new PhantomReference<>(cls[0], rq);
-        cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3" + id), null);
+        cls[0] = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_3" + id), null);
         mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
 
         do {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/defineAnonClass/DefineAnon.java	Wed Sep 07 07:19:48 2016 -0400
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test DefineAnon
+ * @bug 8058575
+ * @library /testlibrary
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.management
+ * @compile -XDignore.symbol.file=true DefineAnon.java
+ * @run main/othervm p1.DefineAnon
+ */
+
+package p1;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import sun.misc.Unsafe;
+
+
+class T {
+    static           protected void test0() { System.out.println("test0 (public)"); }
+    static           protected void test1() { System.out.println("test1 (protected)"); }
+    static /*package-private*/ void test2() { System.out.println("test2 (package)"); }
+    static             private void test3() { System.out.println("test3 (private)"); }
+}
+
+public class DefineAnon {
+
+    private static Unsafe getUnsafe() {
+        try {
+            java.lang.reflect.Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
+            singleoneInstanceField.setAccessible(true);
+            return (Unsafe) singleoneInstanceField.get(null);
+        } catch (Throwable ex) {
+            throw new RuntimeException("Was unable to get Unsafe instance.");
+        }
+    }
+
+    static Unsafe UNSAFE = DefineAnon.getUnsafe();
+
+    static Class<?> getAnonClass(Class<?> hostClass, final String className) {
+        final String superName = "java/lang/Object";
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null);
+
+        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, "test", "()V", null, null);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test0", "()V", false);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test1", "()V", false);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test2", "()V", false);
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test3", "()V", false);
+        mv.visitInsn(Opcodes.RETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+
+        final byte[] classBytes = cw.toByteArray();
+        Class<?> invokerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, new Object[0]);
+        UNSAFE.ensureClassInitialized(invokerClass);
+        return invokerClass;
+    }
+
+    public static void main(String[] args) throws Throwable {
+        Throwable fail = null;
+
+        // Anonymous class has the privileges of its host class, so test[0123] should all work.
+        System.out.println("Injecting from the same package (p1):");
+        Class<?> p1cls = getAnonClass(T.class, "p1/AnonClass");
+        try {
+            p1cls.getMethod("test").invoke(null);
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            fail = ex;  // throw this to make test fail, since subtest failed
+        }
+
+        // Anonymous class has different package name from host class.  Should throw
+        // IllegalArgumentException.
+        System.out.println("Injecting from the wrong package (p2):");
+        try {
+            Class<?> p2cls = getAnonClass(DefineAnon.class, "p2/AnonClass");
+            p2cls.getMethod("test").invoke(null);
+            System.out.println("Failed, did not get expected IllegalArgumentException");
+        } catch (java.lang.IllegalArgumentException e) {
+            if (e.getMessage().contains("Host class p1/DefineAnon and anonymous class p2/AnonClass")) {
+                System.out.println("Got expected IllegalArgumentException: " + e.getMessage());
+            } else {
+                throw new RuntimeException("Unexpected message: " + e.getMessage());
+            }
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            fail = ex;  // throw this to make test fail, since subtest failed
+        }
+
+        // Inject a class in the unnamed package into p1.T.  It should be able
+        // to access all methods in p1.T.
+        System.out.println("Injecting unnamed package into correct host class:");
+        try {
+            Class<?> p3cls = getAnonClass(T.class, "AnonClass");
+            p3cls.getMethod("test").invoke(null);
+        } catch (Throwable ex) {
+            ex.printStackTrace();
+            fail = ex;  // throw this to make test fail, since subtest failed
+        }
+
+        // Try using an array class as the host class.  This should throw IllegalArgumentException.
+        try {
+            Class<?> p3cls = getAnonClass(String[].class, "AnonClass");
+            throw new RuntimeException("Expected IllegalArgumentException not thrown");
+        } catch (IllegalArgumentException ex) {
+        }
+
+        if (fail != null) throw fail;  // make test fail, since subtest failed
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/defineAnonClass/NestedUnsafe.java	Wed Sep 07 07:19:48 2016 -0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058575
+ * @summary Creates an anonymous class inside of an anonymous class.
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.compiler
+ *          java.management
+ * @run main p.NestedUnsafe
+ */
+
+package p;
+
+import java.security.ProtectionDomain;
+import java.io.InputStream;
+import java.lang.*;
+import jdk.test.lib.*;
+import jdk.internal.misc.Unsafe;
+import jdk.test.lib.unsafe.UnsafeHelper;
+
+
+// Test that an anonymous class in package 'p' cannot define its own anonymous class
+// in another package.
+public class NestedUnsafe {
+    // The String concatenation should create the nested anonymous class.
+    static byte klassbuf[] = InMemoryJavaCompiler.compile("q.TestClass",
+        "package q; " +
+        "public class TestClass { " +
+        "    public static void concat(String one, String two) throws Throwable { " +
+        "        System.out.println(one + two);" +
+        " } } ");
+
+    public static void main(String args[]) throws Exception {
+        Unsafe unsafe = UnsafeHelper.getUnsafe();
+
+        // The anonymous class calls defineAnonymousClass creating a nested anonymous class.
+        byte klassbuf2[] = InMemoryJavaCompiler.compile("p.TestClass2",
+            "package p; " +
+            "import jdk.internal.misc.Unsafe; " +
+            "public class TestClass2 { " +
+            "    public static void doit() throws Throwable { " +
+            "        Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); " +
+            "        Class klass2 = unsafe.defineAnonymousClass(TestClass2.class, p.NestedUnsafe.klassbuf, new Object[0]); " +
+            "        unsafe.ensureClassInitialized(klass2); " +
+            "        Class[] dArgs = new Class[2]; " +
+            "        dArgs[0] = String.class; " +
+            "        dArgs[1] = String.class; " +
+            "        try { " +
+            "            klass2.getMethod(\"concat\", dArgs).invoke(null, \"CC\", \"DD\"); " +
+            "        } catch (Throwable ex) { " +
+            "            throw new RuntimeException(\"Exception: \" + ex.toString()); " +
+            "        } " +
+            "} } ",
+            "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED");
+
+        Class klass2 = unsafe.defineAnonymousClass(p.NestedUnsafe.class, klassbuf2, new Object[0]);
+        try {
+            klass2.getMethod("doit").invoke(null);
+            throw new RuntimeException("Expected exception not thrown");
+        } catch (Throwable ex) {
+            Throwable iae = ex.getCause();
+            if (!iae.toString().contains(
+                "IllegalArgumentException: Host class p/NestedUnsafe and anonymous class q/TestClass")) {
+                throw new RuntimeException("Exception: " + iae.toString());
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/runtime/defineAnonClass/NestedUnsafe2.java	Wed Sep 07 07:19:48 2016 -0400
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058575
+ * @summary Creates an anonymous class inside of an anonymous class.
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.compiler
+ *          java.management
+ * @run main p.NestedUnsafe2
+ */
+
+package p;
+
+import java.security.ProtectionDomain;
+import java.io.InputStream;
+import java.lang.*;
+import jdk.test.lib.*;
+import jdk.internal.misc.Unsafe;
+import jdk.test.lib.unsafe.UnsafeHelper;
+
+
+// Test that an anonymous class that gets put in its host's package cannot define
+// an anonymous class in another package.
+public class NestedUnsafe2 {
+    // The String concatenation should create the nested anonymous class.
+    public static byte klassbuf[] = InMemoryJavaCompiler.compile("q.TestClass",
+        "package q; " +
+        "public class TestClass { " +
+        "    public static void concat(String one, String two) throws Throwable { " +
+        "        System.out.println(one + two);" +
+        " } } ");
+
+    public static void main(String args[]) throws Exception {
+        Unsafe unsafe = UnsafeHelper.getUnsafe();
+
+        // The anonymous class calls defineAnonymousClass creating a nested anonymous class.
+        byte klassbuf2[] = InMemoryJavaCompiler.compile("TestClass2",
+            "import jdk.internal.misc.Unsafe; " +
+            "public class TestClass2 { " +
+            "    public static void doit() throws Throwable { " +
+            "        Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); " +
+            "        Class klass2 = unsafe.defineAnonymousClass(TestClass2.class, p.NestedUnsafe2.klassbuf, new Object[0]); " +
+            "        unsafe.ensureClassInitialized(klass2); " +
+            "        Class[] dArgs = new Class[2]; " +
+            "        dArgs[0] = String.class; " +
+            "        dArgs[1] = String.class; " +
+            "        try { " +
+            "            klass2.getMethod(\"concat\", dArgs).invoke(null, \"CC\", \"DD\"); " +
+            "        } catch (Throwable ex) { " +
+            "            throw new RuntimeException(\"Exception: \" + ex.toString()); " +
+            "        } " +
+            "} } ",
+            "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED");
+
+        Class klass2 = unsafe.defineAnonymousClass(p.NestedUnsafe2.class, klassbuf2, new Object[0]);
+        try {
+            klass2.getMethod("doit").invoke(null);
+            throw new RuntimeException("Expected exception not thrown");
+        } catch (Throwable ex) {
+            Throwable iae = ex.getCause();
+            if (!iae.toString().contains(
+                "IllegalArgumentException: Host class p/NestedUnsafe2 and anonymous class q/TestClass")) {
+                throw new RuntimeException("Exception: " + iae.toString());
+            }
+        }
+    }
+}