ART: Make verifier check invocation args of unresolved methods
In the case of unresolvable methods it is not enough to bail out
with a soft verifier failure, as the compiler relies on the
invocation being well-formed. Check all arguments against the
proto id in such a case.
Also make sure to complete parsing arguments in the case the
method was resolved.
Refactored code to unite these two cases.
Bug: 15616104
Change-Id: I5ec58cbfbf6b09eaaf07ddf15edae7c6a8b62b17
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 2f4e805..c9c3bba 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3006,6 +3006,164 @@
return res_method;
}
+template <class T>
+mirror::ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator(T* it, const Instruction* inst,
+ MethodType method_type,
+ bool is_range,
+ mirror::ArtMethod* res_method) {
+ // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
+ // match the call to the signature. Also, we might be calling through an abstract method
+ // definition (which doesn't have register count values).
+ const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
+ /* caught by static verifier */
+ DCHECK(is_range || expected_args <= 5);
+ if (expected_args > code_item_->outs_size_) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args
+ << ") exceeds outsSize (" << code_item_->outs_size_ << ")";
+ return nullptr;
+ }
+
+ uint32_t arg[5];
+ if (!is_range) {
+ inst->GetVarArgs(arg);
+ }
+ uint32_t sig_registers = 0;
+
+ /*
+ * Check the "this" argument, which must be an instance of the class that declared the method.
+ * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
+ * rigorous check here (which is okay since we have to do it at runtime).
+ */
+ if (method_type != METHOD_STATIC) {
+ const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
+ if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
+ CHECK(have_pending_hard_failure_);
+ return nullptr;
+ }
+ if (actual_arg_type.IsUninitializedReference()) {
+ if (res_method) {
+ if (!res_method->IsConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
+ return nullptr;
+ }
+ } else {
+ // Check whether the name of the called method is "<init>"
+ const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ if (strcmp(dex_file_->GetMethodName(dex_file_->GetMethodId(method_idx)), "init") != 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
+ return nullptr;
+ }
+ }
+ }
+ if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
+ const RegType* res_method_class;
+ if (res_method != nullptr) {
+ mirror::Class* klass = res_method->GetDeclaringClass();
+ res_method_class = ®_types_.FromClass(klass->GetDescriptor().c_str(), klass,
+ klass->CannotBeAssignedFromOtherTypes());
+ } else {
+ const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
+ res_method_class = ®_types_.FromDescriptor(class_loader_->Get(),
+ dex_file_->StringByTypeIdx(class_idx),
+ false);
+ }
+ if (!res_method_class->IsAssignableFrom(actual_arg_type)) {
+ Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS:
+ VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
+ << "' not instance of '" << *res_method_class << "'";
+ // Continue on soft failures. We need to find possible hard failures to avoid problems in
+ // the compiler.
+ if (have_pending_hard_failure_) {
+ return nullptr;
+ }
+ }
+ }
+ sig_registers = 1;
+ }
+
+ for ( ; it->HasNext(); it->Next()) {
+ if (sig_registers >= expected_args) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation, expected " << inst->VRegA() <<
+ " arguments, found " << sig_registers << " or more.";
+ return nullptr;
+ }
+
+ const char* param_descriptor = it->GetDescriptor();
+
+ if (param_descriptor == nullptr) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation because of missing signature "
+ "component";
+ return nullptr;
+ }
+
+ const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), param_descriptor,
+ false);
+ uint32_t get_reg = is_range ? inst->VRegC_3rc() + static_cast<uint32_t>(sig_registers) :
+ arg[sig_registers];
+ if (reg_type.IsIntegralTypes()) {
+ const RegType& src_type = work_line_->GetRegisterType(get_reg);
+ if (!src_type.IsIntegralTypes()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << get_reg << " has type " << src_type
+ << " but expected " << reg_type;
+ return res_method;
+ }
+ } else if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
+ // Continue on soft failures. We need to find possible hard failures to avoid problems in the
+ // compiler.
+ if (have_pending_hard_failure_) {
+ return res_method;
+ }
+ }
+ sig_registers += reg_type.IsLongOrDoubleTypes() ? 2 : 1;
+ }
+ if (expected_args != sig_registers) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation, expected " << expected_args <<
+ " arguments, found " << sig_registers;
+ return nullptr;
+ }
+ return res_method;
+}
+
+void MethodVerifier::VerifyInvocationArgsUnresolvedMethod(const Instruction* inst,
+ MethodType method_type,
+ bool is_range) {
+ // As the method may not have been resolved, make this static check against what we expect.
+ // The main reason for this code block is to fail hard when we find an illegal use, e.g.,
+ // wrong number of arguments or wrong primitive types, even if the method could not be resolved.
+ const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ DexFileParameterIterator it(*dex_file_,
+ dex_file_->GetProtoId(dex_file_->GetMethodId(method_idx).proto_idx_));
+ VerifyInvocationArgsFromIterator<DexFileParameterIterator>(&it, inst, method_type, is_range,
+ nullptr);
+}
+
+class MethodParamListDescriptorIterator {
+ public:
+ explicit MethodParamListDescriptorIterator(mirror::ArtMethod* res_method) :
+ res_method_(res_method), pos_(0), params_(res_method->GetParameterTypeList()),
+ params_size_(params_ == nullptr ? 0 : params_->Size()) {
+ }
+
+ bool HasNext() {
+ return pos_ < params_size_;
+ }
+
+ void Next() {
+ ++pos_;
+ }
+
+ const char* GetDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return res_method_->GetTypeDescriptorFromTypeIdx(params_->GetTypeItem(pos_).type_idx_);
+ }
+
+ private:
+ mirror::ArtMethod* res_method_;
+ size_t pos_;
+ const DexFile::TypeList* params_;
+ const size_t params_size_;
+};
+
mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst,
MethodType method_type,
bool is_range,
@@ -3014,28 +3172,13 @@
// we're making.
const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- // As the method may not have been resolved, make this static check against what we expect.
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- uint32_t shorty_idx = dex_file_->GetProtoId(method_id.proto_idx_).shorty_idx_;
- uint32_t shorty_len;
- const char* descriptor = dex_file_->StringDataAndUtf16LengthByIdx(shorty_idx, &shorty_len);
- int32_t sig_registers = method_type == METHOD_STATIC ? 0 : 1;
- for (size_t i = 1; i < shorty_len; i++) {
- if (descriptor[i] == 'J' || descriptor[i] == 'D') {
- sig_registers += 2;
- } else {
- sig_registers++;
- }
- }
- if (inst->VRegA() != sig_registers) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation, expected " << inst->VRegA() <<
- " arguments, found " << sig_registers;
- return nullptr;
- }
-
mirror::ArtMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type);
if (res_method == NULL) { // error or class is unresolved
- return NULL;
+ // Check what we can statically.
+ if (!have_pending_hard_failure_) {
+ VerifyInvocationArgsUnresolvedMethod(inst, method_type, is_range);
+ }
+ return nullptr;
}
// If we're using invoke-super(method), make sure that the executing method's class' superclass
@@ -3047,7 +3190,7 @@
Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
<< PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << PrettyMethod(res_method);
- return NULL;
+ return nullptr;
}
mirror::Class* super_klass = super.GetClass();
if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
@@ -3056,95 +3199,14 @@
<< " to super " << super
<< "." << res_method->GetName()
<< res_method->GetSignature();
- return NULL;
+ return nullptr;
}
}
- // We use vAA as our expected arg count, rather than res_method->insSize, because we need to
- // match the call to the signature. Also, we might be calling through an abstract method
- // definition (which doesn't have register count values).
- const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
- /* caught by static verifier */
- DCHECK(is_range || expected_args <= 5);
- if (expected_args > code_item_->outs_size_) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args
- << ") exceeds outsSize (" << code_item_->outs_size_ << ")";
- return NULL;
- }
- /*
- * Check the "this" argument, which must be an instance of the class that declared the method.
- * For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
- * rigorous check here (which is okay since we have to do it at runtime).
- */
- size_t actual_args = 0;
- if (!res_method->IsStatic()) {
- const RegType& actual_arg_type = work_line_->GetInvocationThis(inst, is_range);
- if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
- return NULL;
- }
- if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
- return NULL;
- }
- if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
- mirror::Class* klass = res_method->GetDeclaringClass();
- const RegType& res_method_class =
- reg_types_.FromClass(klass->GetDescriptor().c_str(), klass,
- klass->CannotBeAssignedFromOtherTypes());
- if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
- Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS:
- VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
- << "' not instance of '" << res_method_class << "'";
- return NULL;
- }
- }
- actual_args++;
- }
- /*
- * Process the target method's signature. This signature may or may not
- * have been verified, so we can't assume it's properly formed.
- */
- const DexFile::TypeList* params = res_method->GetParameterTypeList();
- size_t params_size = params == NULL ? 0 : params->Size();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetVarArgs(arg);
- }
- for (size_t param_index = 0; param_index < params_size; param_index++) {
- if (actual_args >= expected_args) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invalid call to '" << PrettyMethod(res_method)
- << "'. Expected " << expected_args << " arguments, processing argument " << actual_args
- << " (where longs/doubles count twice).";
- return NULL;
- }
- const char* descriptor =
- res_method->GetTypeDescriptorFromTypeIdx(params->GetTypeItem(param_index).type_idx_);
- if (descriptor == NULL) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
- << " missing signature component";
- return NULL;
- }
- const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->Get(), descriptor, false);
- uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args];
- if (reg_type.IsIntegralTypes()) {
- const RegType& src_type = work_line_->GetRegisterType(get_reg);
- if (!src_type.IsIntegralTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << get_reg << " has type " << src_type
- << " but expected " << reg_type;
- return res_method;
- }
- } else if (!work_line_->VerifyRegisterType(get_reg, reg_type)) {
- return res_method;
- }
- actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1;
- }
- if (actual_args != expected_args) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation of " << PrettyMethod(res_method)
- << " expected " << expected_args << " arguments, found " << actual_args;
- return NULL;
- } else {
- return res_method;
- }
+ // Process the target method's signature. This signature may or may not
+ MethodParamListDescriptorIterator it(res_method);
+ return VerifyInvocationArgsFromIterator<MethodParamListDescriptorIterator>(&it, inst, method_type,
+ is_range, res_method);
}
mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst,
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 451c9e2..b6d5b35 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -565,6 +565,18 @@
bool is_range, bool is_super)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Similar checks to the above, but on the proto. Will be used when the method cannot be
+ // resolved.
+ void VerifyInvocationArgsUnresolvedMethod(const Instruction* inst, MethodType method_type,
+ bool is_range)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <class T>
+ mirror::ArtMethod* VerifyInvocationArgsFromIterator(T* it, const Instruction* inst,
+ MethodType method_type, bool is_range,
+ mirror::ArtMethod* res_method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
mirror::ArtMethod* GetQuickInvokedMethod(const Instruction* inst,
RegisterLine* reg_line,
bool is_range)