diff options
author | Andreas Gampe <agampe@google.com> | 2014-06-17 15:58:28 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-06-17 04:51:41 +0000 |
commit | 38510acc063ca32c2a842101d3d0006a0b2a4931 (patch) | |
tree | 848a8463e94a348916901c0eaa7d9ca9ca2fde36 /runtime | |
parent | fbb2ea0bc20aa2d11abb0ac50df40fbb3bb1a6f2 (diff) | |
parent | 95c0bf8fb5847cff263639f889d04c7c3c26eedd (diff) | |
download | art-38510acc063ca32c2a842101d3d0006a0b2a4931.zip art-38510acc063ca32c2a842101d3d0006a0b2a4931.tar.gz art-38510acc063ca32c2a842101d3d0006a0b2a4931.tar.bz2 |
Merge "ART: Make verifier check invocation args of unresolved methods"
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/verifier/method_verifier.cc | 266 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.h | 12 |
2 files changed, 176 insertions, 102 deletions
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,59 +3006,11 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth return res_method; } -mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, - MethodType method_type, - bool is_range, - bool is_super) { - // Resolve the method. This could be an abstract or concrete method depending on what sort of call - // 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; - } - - // If we're using invoke-super(method), make sure that the executing method's class' superclass - // has a vtable entry for the target method. - if (is_super) { - DCHECK(method_type == METHOD_VIRTUAL); - const RegType& super = GetDeclaringClass().GetSuperClass(®_types_); - if (super.IsUnresolvedTypes()) { - 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; - } - mirror::Class* super_klass = super.GetClass(); - if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) { - Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " - << PrettyMethod(dex_method_idx_, *dex_file_) - << " to super " << super - << "." << res_method->GetName() - << res_method->GetSignature(); - return NULL; - } - } +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). @@ -3068,83 +3020,193 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, 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; + 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). */ - size_t actual_args = 0; - if (!res_method->IsStatic()) { + if (method_type != METHOD_STATIC) { 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; + 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()) { - 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)) { + 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 << "'"; - return NULL; + << "' 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; + } } } - 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); + sig_registers = 1; } - 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; + + 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* 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 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(), descriptor, false); - uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; + + 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; + << " but expected " << reg_type; return res_method; } } else if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { - return res_method; + // 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; + } } - actual_args = reg_type.IsLongOrDoubleTypes() ? actual_args + 2 : actual_args + 1; + sig_registers += reg_type.IsLongOrDoubleTypes() ? 2 : 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; + 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, + bool is_super) { + // Resolve the method. This could be an abstract or concrete method depending on what sort of call + // we're making. + const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + + mirror::ArtMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type); + if (res_method == NULL) { // error or class is unresolved + // 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 + // has a vtable entry for the target method. + if (is_super) { + DCHECK(method_type == METHOD_VIRTUAL); + const RegType& super = GetDeclaringClass().GetSuperClass(®_types_); + if (super.IsUnresolvedTypes()) { + Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from " + << PrettyMethod(dex_method_idx_, *dex_file_) + << " to super " << PrettyMethod(res_method); + return nullptr; + } + mirror::Class* super_klass = super.GetClass(); + if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) { + Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " + << PrettyMethod(dex_method_idx_, *dex_file_) + << " to super " << super + << "." << res_method->GetName() + << res_method->GetSignature(); + return nullptr; + } + } + + // 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 @@ class MethodVerifier { 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) |