mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8058575: IllegalAccessError trying to access package-private class from VM anonymous class
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
This commit is contained in:
parent
c271d83599
commit
e8e6415b7a
14 changed files with 416 additions and 31 deletions
|
@ -5407,6 +5407,59 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
|
||||||
debug_only(ik->verify();)
|
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) {
|
static bool relax_format_check_for(ClassLoaderData* loader_data) {
|
||||||
bool trusted = (loader_data->is_the_null_class_loader_data() ||
|
bool trusted = (loader_data->is_the_null_class_loader_data() ||
|
||||||
SystemDictionary::is_platform_class_loader(loader_data->class_loader()));
|
SystemDictionary::is_platform_class_loader(loader_data->class_loader()));
|
||||||
|
@ -5422,7 +5475,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
||||||
Symbol* name,
|
Symbol* name,
|
||||||
ClassLoaderData* loader_data,
|
ClassLoaderData* loader_data,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
Publicity pub_level,
|
Publicity pub_level,
|
||||||
TRAPS) :
|
TRAPS) :
|
||||||
|
@ -5697,6 +5750,13 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
|
||||||
return;
|
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
|
// Verification prevents us from creating names with dots in them, this
|
||||||
// asserts that that's the case.
|
// asserts that that's the case.
|
||||||
assert(is_internal_format(_class_name), "external class name format used internally");
|
assert(is_internal_format(_class_name), "external class name format used internally");
|
||||||
|
|
|
@ -79,7 +79,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||||
const Symbol* _requested_name;
|
const Symbol* _requested_name;
|
||||||
Symbol* _class_name;
|
Symbol* _class_name;
|
||||||
mutable ClassLoaderData* _loader_data;
|
mutable ClassLoaderData* _loader_data;
|
||||||
const Klass* _host_klass;
|
const InstanceKlass* _host_klass;
|
||||||
GrowableArray<Handle>* _cp_patches; // overrides for CP entries
|
GrowableArray<Handle>* _cp_patches; // overrides for CP entries
|
||||||
|
|
||||||
// Metadata created before the instance klass is created. Must be deallocated
|
// Metadata created before the instance klass is created. Must be deallocated
|
||||||
|
@ -155,6 +155,9 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||||
ConstantPool* cp,
|
ConstantPool* cp,
|
||||||
TRAPS);
|
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 fill_instance_klass(InstanceKlass* ik, bool cf_changed_in_CFLH, TRAPS);
|
||||||
void set_klass(InstanceKlass* instance);
|
void set_klass(InstanceKlass* instance);
|
||||||
|
|
||||||
|
@ -474,7 +477,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||||
Symbol* name,
|
Symbol* name,
|
||||||
ClassLoaderData* loader_data,
|
ClassLoaderData* loader_data,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
Publicity pub_level,
|
Publicity pub_level,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
@ -500,7 +503,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
|
||||||
bool is_anonymous() const { return _host_klass != NULL; }
|
bool is_anonymous() const { return _host_klass != NULL; }
|
||||||
bool is_interface() const { return _access_flags.is_interface(); }
|
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; }
|
const GrowableArray<Handle>* cp_patches() const { return _cp_patches; }
|
||||||
ClassLoaderData* loader_data() const { return _loader_data; }
|
ClassLoaderData* loader_data() const { return _loader_data; }
|
||||||
const Symbol* class_name() const { return _class_name; }
|
const Symbol* class_name() const { return _class_name; }
|
||||||
|
|
|
@ -94,7 +94,7 @@ instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,
|
||||||
Symbol* name,
|
Symbol* name,
|
||||||
ClassLoaderData* loader_data,
|
ClassLoaderData* loader_data,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ class KlassFactory : AllStatic {
|
||||||
Symbol* name,
|
Symbol* name,
|
||||||
ClassLoaderData* loader_data,
|
ClassLoaderData* loader_data,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1027,7 +1027,7 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name,
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
ClassFileStream* st,
|
ClassFileStream* st,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ public:
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
ClassFileStream* st,
|
ClassFileStream* st,
|
||||||
const Klass* host_klass,
|
const InstanceKlass* host_klass,
|
||||||
GrowableArray<Handle>* cp_patches,
|
GrowableArray<Handle>* cp_patches,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
|
|
|
@ -2786,7 +2786,7 @@ void ClassVerifier::verify_invoke_instructions(
|
||||||
// direct interface relative to the host class
|
// direct interface relative to the host class
|
||||||
have_imr_indirect = (have_imr_indirect &&
|
have_imr_indirect = (have_imr_indirect &&
|
||||||
!is_same_or_direct_interface(
|
!is_same_or_direct_interface(
|
||||||
InstanceKlass::cast(current_class()->host_klass()),
|
current_class()->host_klass(),
|
||||||
host_klass_type, ref_class_type));
|
host_klass_type, ref_class_type));
|
||||||
}
|
}
|
||||||
if (!subtype) {
|
if (!subtype) {
|
||||||
|
|
|
@ -619,8 +619,8 @@ class InstanceKlass: public Klass {
|
||||||
objArrayOop signers() const;
|
objArrayOop signers() const;
|
||||||
|
|
||||||
// host class
|
// host class
|
||||||
Klass* host_klass() const {
|
InstanceKlass* host_klass() const {
|
||||||
Klass** hk = (Klass**)adr_host_klass();
|
InstanceKlass** hk = adr_host_klass();
|
||||||
if (hk == NULL) {
|
if (hk == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
@ -628,9 +628,9 @@ class InstanceKlass: public Klass {
|
||||||
return *hk;
|
return *hk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void set_host_klass(const Klass* host) {
|
void set_host_klass(const InstanceKlass* host) {
|
||||||
assert(is_anonymous(), "not anonymous");
|
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");
|
assert(addr != NULL, "no reversed space");
|
||||||
if (addr != NULL) {
|
if (addr != NULL) {
|
||||||
*addr = host;
|
*addr = host;
|
||||||
|
@ -1057,13 +1057,13 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Klass** adr_host_klass() const {
|
InstanceKlass** adr_host_klass() const {
|
||||||
if (is_anonymous()) {
|
if (is_anonymous()) {
|
||||||
Klass** adr_impl = adr_implementor();
|
InstanceKlass** adr_impl = (InstanceKlass **)adr_implementor();
|
||||||
if (adr_impl != NULL) {
|
if (adr_impl != NULL) {
|
||||||
return adr_impl + 1;
|
return adr_impl + 1;
|
||||||
} else {
|
} else {
|
||||||
return end_of_nonstatic_oop_maps();
|
return (InstanceKlass **)end_of_nonstatic_oop_maps();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -783,6 +783,7 @@ UNSAFE_ENTRY(jclass, Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring na
|
||||||
|
|
||||||
// define a class but do not make it known to the class loader or system dictionary
|
// 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
|
// - 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)
|
// - 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
|
// - cp_patches: where non-null entries exist, they replace corresponding CP entries in data
|
||||||
|
|
||||||
|
@ -791,8 +792,12 @@ UNSAFE_ENTRY(jclass, Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring na
|
||||||
// link to any member of U. Just after U is loaded, the only way to use it is reflectively,
|
// 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.
|
// 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.
|
// 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.
|
// 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
|
// 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
|
// allow language implementations to simulate "open classes"; a host class in effect gets
|
||||||
|
@ -874,9 +879,11 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
|
||||||
|
|
||||||
// Primitive types have NULL Klass* fields in their java.lang.Class instances.
|
// Primitive types have NULL Klass* fields in their java.lang.Class instances.
|
||||||
if (host_klass == NULL) {
|
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();
|
const char* host_source = host_klass->external_name();
|
||||||
Handle host_loader(THREAD, host_klass->class_loader());
|
Handle host_loader(THREAD, host_klass->class_loader());
|
||||||
Handle host_domain(THREAD, host_klass->protection_domain());
|
Handle host_domain(THREAD, host_klass->protection_domain());
|
||||||
|
@ -907,7 +914,7 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
|
||||||
host_loader,
|
host_loader,
|
||||||
host_domain,
|
host_domain,
|
||||||
&st,
|
&st,
|
||||||
host_klass,
|
InstanceKlass::cast(host_klass),
|
||||||
cp_patches,
|
cp_patches,
|
||||||
CHECK_NULL);
|
CHECK_NULL);
|
||||||
if (anonk == NULL) {
|
if (anonk == NULL) {
|
||||||
|
|
|
@ -412,13 +412,13 @@ oop Reflection::array_component_type(oop mirror, TRAPS) {
|
||||||
return result;
|
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);
|
DEBUG_ONLY(int inf_loop_check = 1000 * 1000 * 1000);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const Klass* hc = (const Klass*)ik->host_klass();
|
const InstanceKlass* hc = ik->host_klass();
|
||||||
if (hc == NULL) return false;
|
if (hc == NULL) return false;
|
||||||
if (hc == host_klass) return true;
|
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.
|
// 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.
|
// Therefore there cannot be a loop here unless there's another bug.
|
||||||
|
@ -436,8 +436,8 @@ static bool can_relax_access_check_for(const Klass* accessor,
|
||||||
|
|
||||||
// If either is on the other's host_klass chain, access is OK,
|
// If either is on the other's host_klass chain, access is OK,
|
||||||
// because one is inside the other.
|
// because one is inside the other.
|
||||||
if (under_host_klass(accessor_ik, accessee) ||
|
if (under_host_klass(accessor_ik, accessee_ik) ||
|
||||||
under_host_klass(accessee_ik, accessor))
|
under_host_klass(accessee_ik, accessor_ik))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if ((RelaxAccessControlCheck &&
|
if ((RelaxAccessControlCheck &&
|
||||||
|
|
|
@ -62,7 +62,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
|
||||||
public class CallSiteDepContextTest {
|
public class CallSiteDepContextTest {
|
||||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||||
static final MethodHandles.Lookup LOOKUP = MethodHandleHelper.IMPL_LOOKUP;
|
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 String METHOD_NAME = "m";
|
||||||
static final MethodType TYPE = MethodType.methodType(int.class);
|
static final MethodType TYPE = MethodType.methodType(int.class);
|
||||||
|
|
||||||
|
@ -129,8 +129,8 @@ public class CallSiteDepContextTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void testSharedCallSite() throws Throwable {
|
public static void testSharedCallSite() throws Throwable {
|
||||||
Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
|
Class<?> cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_1"), null);
|
||||||
Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
|
Class<?> cls2 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_2"), null);
|
||||||
|
|
||||||
MethodHandle[] mhs = new MethodHandle[] {
|
MethodHandle[] mhs = new MethodHandle[] {
|
||||||
LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
|
LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
|
||||||
|
@ -151,7 +151,7 @@ public class CallSiteDepContextTest {
|
||||||
execute(1, mh);
|
execute(1, mh);
|
||||||
|
|
||||||
// mcs.context == cls1
|
// 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);
|
MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
|
||||||
|
|
||||||
execute(1, mh1);
|
execute(1, mh1);
|
||||||
|
@ -170,8 +170,8 @@ public class CallSiteDepContextTest {
|
||||||
mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
|
mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
|
||||||
|
|
||||||
Class<?>[] cls = new Class[] {
|
Class<?>[] cls = new Class[] {
|
||||||
UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1" + id), null),
|
UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_1" + id), null),
|
||||||
UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2" + id), null),
|
UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_2" + id), null),
|
||||||
};
|
};
|
||||||
|
|
||||||
MethodHandle[] mhs = new MethodHandle[] {
|
MethodHandle[] mhs = new MethodHandle[] {
|
||||||
|
@ -185,7 +185,7 @@ public class CallSiteDepContextTest {
|
||||||
execute(1, mhs);
|
execute(1, mhs);
|
||||||
|
|
||||||
ref = new PhantomReference<>(cls[0], rq);
|
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);
|
mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
134
hotspot/test/runtime/defineAnonClass/DefineAnon.java
Normal file
134
hotspot/test/runtime/defineAnonClass/DefineAnon.java
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
91
hotspot/test/runtime/defineAnonClass/NestedUnsafe.java
Normal file
91
hotspot/test/runtime/defineAnonClass/NestedUnsafe.java
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
hotspot/test/runtime/defineAnonClass/NestedUnsafe2.java
Normal file
90
hotspot/test/runtime/defineAnonClass/NestedUnsafe2.java
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue