8350029: Illegal invokespecial interface not caught by verification
Reviewed-by: coleenp, matsaave
This commit is contained in:
parent
9186cc7310
commit
8f8b367ae3
@ -48,41 +48,50 @@ VerificationType VerificationType::from_tag(u1 tag) {
|
||||
}
|
||||
}
|
||||
|
||||
bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) {
|
||||
// Potentially resolve the target class and from class, and check whether the from class is assignable
|
||||
// to the target class. The current_klass is the class being verified - it could also be the target in some
|
||||
// cases, and otherwise is needed to obtain the correct classloader for resolving the other classes.
|
||||
bool VerificationType::resolve_and_check_assignability(InstanceKlass* current_klass, Symbol* target_name, Symbol* from_name,
|
||||
bool from_field_is_protected, bool from_is_array,
|
||||
bool from_is_object, bool* target_is_interface, TRAPS) {
|
||||
HandleMark hm(THREAD);
|
||||
Klass* this_class;
|
||||
if (klass->is_hidden() && klass->name() == name) {
|
||||
this_class = klass;
|
||||
Klass* target_klass;
|
||||
if (current_klass->is_hidden() && current_klass->name() == target_name) {
|
||||
target_klass = current_klass;
|
||||
} else {
|
||||
this_class = SystemDictionary::resolve_or_fail(
|
||||
name, Handle(THREAD, klass->class_loader()), true, CHECK_false);
|
||||
target_klass = SystemDictionary::resolve_or_fail(
|
||||
target_name, Handle(THREAD, current_klass->class_loader()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(this_class, klass);
|
||||
Verifier::trace_class_resolution(target_klass, current_klass);
|
||||
}
|
||||
}
|
||||
|
||||
if (this_class->is_interface() && (!from_field_is_protected ||
|
||||
bool is_intf = target_klass->is_interface();
|
||||
if (target_is_interface != nullptr) {
|
||||
*target_is_interface = is_intf;
|
||||
}
|
||||
|
||||
if (is_intf && (!from_field_is_protected ||
|
||||
from_name != vmSymbols::java_lang_Object())) {
|
||||
// If we are not trying to access a protected field or method in
|
||||
// java.lang.Object then, for arrays, we only allow assignability
|
||||
// to interfaces java.lang.Cloneable and java.io.Serializable.
|
||||
// Otherwise, we treat interfaces as java.lang.Object.
|
||||
return !from_is_array ||
|
||||
this_class == vmClasses::Cloneable_klass() ||
|
||||
this_class == vmClasses::Serializable_klass();
|
||||
target_klass == vmClasses::Cloneable_klass() ||
|
||||
target_klass == vmClasses::Serializable_klass();
|
||||
} else if (from_is_object) {
|
||||
Klass* from_class;
|
||||
if (klass->is_hidden() && klass->name() == from_name) {
|
||||
from_class = klass;
|
||||
Klass* from_klass;
|
||||
if (current_klass->is_hidden() && current_klass->name() == from_name) {
|
||||
from_klass = current_klass;
|
||||
} else {
|
||||
from_class = SystemDictionary::resolve_or_fail(
|
||||
from_name, Handle(THREAD, klass->class_loader()), true, CHECK_false);
|
||||
from_klass = SystemDictionary::resolve_or_fail(
|
||||
from_name, Handle(THREAD, current_klass->class_loader()), true, CHECK_false);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Verifier::trace_class_resolution(from_class, klass);
|
||||
Verifier::trace_class_resolution(from_klass, current_klass);
|
||||
}
|
||||
}
|
||||
return from_class->is_subclass_of(this_class);
|
||||
return from_klass->is_subclass_of(target_klass);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -90,8 +99,8 @@ bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Sym
|
||||
|
||||
bool VerificationType::is_reference_assignable_from(
|
||||
const VerificationType& from, ClassVerifier* context,
|
||||
bool from_field_is_protected, TRAPS) const {
|
||||
InstanceKlass* klass = context->current_class();
|
||||
bool from_field_is_protected, bool* this_is_interface, TRAPS) const {
|
||||
|
||||
if (from.is_null()) {
|
||||
// null is assignable to any reference
|
||||
return true;
|
||||
@ -109,7 +118,7 @@ bool VerificationType::is_reference_assignable_from(
|
||||
#if INCLUDE_CDS
|
||||
if (CDSConfig::is_dumping_archive()) {
|
||||
bool skip_assignability_check = false;
|
||||
SystemDictionaryShared::add_verification_constraint(klass,
|
||||
SystemDictionaryShared::add_verification_constraint(context->current_class(),
|
||||
name(), from.name(), from_field_is_protected, from.is_array(),
|
||||
from.is_object(), &skip_assignability_check);
|
||||
if (skip_assignability_check) {
|
||||
@ -119,8 +128,9 @@ bool VerificationType::is_reference_assignable_from(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return resolve_and_check_assignability(klass, name(), from.name(),
|
||||
from_field_is_protected, from.is_array(), from.is_object(), THREAD);
|
||||
return resolve_and_check_assignability(context->current_class(), name(), from.name(),
|
||||
from_field_is_protected, from.is_array(),
|
||||
from.is_object(), this_is_interface, THREAD);
|
||||
} else if (is_array() && from.is_array()) {
|
||||
VerificationType comp_this = get_component(context);
|
||||
VerificationType comp_from = from.get_component(context);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2025, 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
|
||||
@ -288,7 +288,7 @@ class VerificationType {
|
||||
if (is_reference() && from.is_reference()) {
|
||||
return is_reference_assignable_from(from, context,
|
||||
from_field_is_protected,
|
||||
THREAD);
|
||||
nullptr, THREAD);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -327,17 +327,24 @@ class VerificationType {
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
|
||||
private:
|
||||
bool is_reference_assignable_from(const VerificationType& from, ClassVerifier* context,
|
||||
bool from_field_is_protected, bool* this_is_interface, TRAPS) const;
|
||||
|
||||
bool is_reference_assignable_from(
|
||||
const VerificationType&, ClassVerifier*, bool from_field_is_protected,
|
||||
TRAPS) const;
|
||||
|
||||
public:
|
||||
static bool resolve_and_check_assignability(InstanceKlass* klass, Symbol* name,
|
||||
static bool resolve_and_check_assignability(InstanceKlass* current_klass, Symbol* target_name,
|
||||
Symbol* from_name, bool from_field_is_protected,
|
||||
bool from_is_array, bool from_is_object,
|
||||
TRAPS) {
|
||||
return resolve_and_check_assignability(current_klass, target_name, from_name, from_field_is_protected,
|
||||
from_is_array, from_is_object, nullptr, THREAD);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool resolve_and_check_assignability(InstanceKlass* current_klass, Symbol* target_name,
|
||||
Symbol* from_name, bool from_field_is_protected,
|
||||
bool from_is_array, bool from_is_object,
|
||||
bool* target_is_interface,
|
||||
TRAPS);
|
||||
|
||||
};
|
||||
|
||||
#endif // SHARE_CLASSFILE_VERIFICATIONTYPE_HPP
|
||||
|
@ -2891,26 +2891,43 @@ void ClassVerifier::verify_invoke_instructions(
|
||||
"Illegal call to internal method");
|
||||
return;
|
||||
}
|
||||
} else if (opcode == Bytecodes::_invokespecial
|
||||
}
|
||||
// invokespecial, when not <init>, must be to a method in the current class, a direct superinterface,
|
||||
// or any superclass (including Object).
|
||||
else if (opcode == Bytecodes::_invokespecial
|
||||
&& !is_same_or_direct_interface(current_class(), current_type(), ref_class_type)
|
||||
&& !ref_class_type.equals(VerificationType::reference_type(
|
||||
current_class()->super()->name()))) {
|
||||
bool subtype = false;
|
||||
bool have_imr_indirect = cp->tag_at(index).value() == JVM_CONSTANT_InterfaceMethodref;
|
||||
subtype = ref_class_type.is_assignable_from(
|
||||
current_type(), this, false, CHECK_VERIFY(this));
|
||||
if (!subtype) {
|
||||
&& !ref_class_type.equals(VerificationType::reference_type(current_class()->super()->name()))) {
|
||||
|
||||
// We know it is not current class, direct superinterface or immediate superclass. That means it
|
||||
// could be:
|
||||
// - a totally unrelated class or interface
|
||||
// - an indirect superinterface
|
||||
// - an indirect superclass (including Object)
|
||||
// We use the assignability test to see if it is a superclass, or else an interface, and keep track
|
||||
// of the latter. Note that subtype can be true if we are dealing with an interface that is not actually
|
||||
// implemented as assignability treats all interfaces as Object.
|
||||
|
||||
bool is_interface = false; // This can only be set true if the assignability check will return true
|
||||
// and we loaded the class. For any other "true" returns (e.g. same class
|
||||
// or Object) we either can't get here (same class already excluded above)
|
||||
// or we know it is not an interface (i.e. Object).
|
||||
bool subtype = ref_class_type.is_reference_assignable_from(current_type(), this, false,
|
||||
&is_interface, CHECK_VERIFY(this));
|
||||
if (!subtype) { // Totally unrelated class
|
||||
verify_error(ErrorContext::bad_code(bci),
|
||||
"Bad invokespecial instruction: "
|
||||
"current class isn't assignable to reference class.");
|
||||
return;
|
||||
} else if (have_imr_indirect) {
|
||||
} else {
|
||||
// Indirect superclass (including Object), indirect interface, or unrelated interface.
|
||||
// Any interface use is an error.
|
||||
if (is_interface) {
|
||||
verify_error(ErrorContext::bad_code(bci),
|
||||
"Bad invokespecial instruction: "
|
||||
"interface method reference is in an indirect superinterface.");
|
||||
"interface method to invoke is not in a direct superinterface.");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Get the verification types for the method's arguments.
|
||||
|
@ -1200,7 +1200,7 @@ Method* LinkResolver::linktime_resolve_special_method(const LinkInfo& link_info,
|
||||
}
|
||||
|
||||
// ensure that invokespecial's interface method reference is in
|
||||
// a direct superinterface, not an indirect superinterface
|
||||
// a direct superinterface, not an indirect superinterface or unrelated interface
|
||||
Klass* current_klass = link_info.current_klass();
|
||||
if (current_klass != nullptr && resolved_klass->is_interface()) {
|
||||
InstanceKlass* klass_to_check = InstanceKlass::cast(current_klass);
|
||||
@ -1209,7 +1209,7 @@ Method* LinkResolver::linktime_resolve_special_method(const LinkInfo& link_info,
|
||||
stringStream ss;
|
||||
ss.print("Interface method reference: '");
|
||||
resolved_method->print_external_name(&ss);
|
||||
ss.print("', is in an indirect superinterface of %s",
|
||||
ss.print("', is not in a direct superinterface of %s",
|
||||
current_klass->external_name());
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_IncompatibleClassChangeError(), ss.as_string());
|
||||
}
|
||||
|
29
test/hotspot/jtreg/runtime/verifier/invokespecial/Run.java
Normal file
29
test/hotspot/jtreg/runtime/verifier/invokespecial/Run.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class with a run()V method that doesn't implement Runnable.
|
||||
*/
|
||||
public class Run {
|
||||
public void run() { }
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 id=verified
|
||||
* @compile Run.java UseMethodRef.jasm UseInterfaceMethodRef.jasm TestInvokeSpecialInterface.java
|
||||
* @run main/othervm TestInvokeSpecialInterface true
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=unverified
|
||||
* @compile Run.java UseMethodRef.jasm UseInterfaceMethodRef.jasm TestInvokeSpecialInterface.java
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:-BytecodeVerificationRemote TestInvokeSpecialInterface false
|
||||
*/
|
||||
|
||||
public class TestInvokeSpecialInterface {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (args[0].equals("true")) {
|
||||
check_verified();
|
||||
} else {
|
||||
check_unverified();
|
||||
}
|
||||
}
|
||||
|
||||
static void check_verified() {
|
||||
String veMsg = "interface method to invoke is not in a direct superinterface";
|
||||
try {
|
||||
UseMethodRef t = new UseMethodRef();
|
||||
UseMethodRef.test(t);
|
||||
}
|
||||
catch(VerifyError ve) {
|
||||
if (ve.getMessage().contains(veMsg)) {
|
||||
System.out.println("Got expected: " + ve);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected VerifyError thrown", ve);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
UseInterfaceMethodRef t = new UseInterfaceMethodRef();
|
||||
UseInterfaceMethodRef.test(t);
|
||||
}
|
||||
catch(VerifyError ve) {
|
||||
if (ve.getMessage().contains(veMsg)) {
|
||||
System.out.println("Got expected: " + ve);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected VerifyError thrown", ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void check_unverified() {
|
||||
try {
|
||||
UseMethodRef t = new UseMethodRef();
|
||||
UseMethodRef.test(t);
|
||||
}
|
||||
catch(IncompatibleClassChangeError icce) {
|
||||
String icceMsg = "Method 'void java.lang.Runnable.run()' must be InterfaceMethodref constant";
|
||||
if (icce.getMessage().contains(icceMsg)) {
|
||||
System.out.println("Got expected: " + icce);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected IncompatibleClassChangeError", icce);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
UseInterfaceMethodRef t = new UseInterfaceMethodRef();
|
||||
UseInterfaceMethodRef.test(t);
|
||||
}
|
||||
catch(IncompatibleClassChangeError icce) {
|
||||
String icceMsg = "Interface method reference: 'void java.lang.Runnable.run()', is not in a direct superinterface of UseInterfaceMethodRef";
|
||||
if (icce.getMessage().contains(icceMsg)) {
|
||||
System.out.println("Got expected: " + icce);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected IncompatibleClassChangeError", icce);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
class UseInterfaceMethodRef extends Run version 52:0 {
|
||||
|
||||
public Method "<init>":"()V" stack 1 locals 1 {
|
||||
aload_0;
|
||||
invokespecial Method Run."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public static Method test:"(LUseInterfaceMethodRef;)V" stack 2 {
|
||||
aload_0;
|
||||
invokespecial InterfaceMethod java/lang/Runnable.run:()V; // VerifyError
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
class UseMethodRef extends Run version 52:0 {
|
||||
|
||||
public Method "<init>":"()V" stack 1 locals 1 {
|
||||
aload_0;
|
||||
invokespecial Method Run."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
public static Method test:"(LUseMethodRef;)V" stack 2 {
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Runnable.run:()V; // VerifyError
|
||||
return;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user