diff options
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 27 | ||||
| -rw-r--r-- | runtime/verifier/reg_type-inl.h | 14 | ||||
| -rw-r--r-- | runtime/verifier/reg_type.cc | 24 | ||||
| -rw-r--r-- | runtime/verifier/reg_type.h | 46 | ||||
| -rw-r--r-- | runtime/verifier/reg_type_cache-inl.h | 3 | ||||
| -rw-r--r-- | runtime/verifier/reg_type_cache.cc | 3 | ||||
| -rw-r--r-- | runtime/verifier/reg_type_cache.h | 4 | ||||
| -rw-r--r-- | runtime/verifier/reg_type_test.cc | 20 |
8 files changed, 116 insertions, 25 deletions
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c6ba2f7e43..978e51d7b0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2237,7 +2237,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected"; } else { /* return_type is the *expected* return type, not register value */ - DCHECK(!return_type.IsZero()); + DCHECK(!return_type.IsZeroOrNull()); DCHECK(!return_type.IsUninitializedReference()); const uint32_t vregA = inst->VRegA_11x(); const RegType& reg_type = work_line_->GetRegisterType(this, vregA); @@ -2485,7 +2485,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::ARRAY_LENGTH: { const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegB_12x()); if (res_type.IsReferenceTypes()) { - if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null + if (!res_type.IsArrayTypes() && !res_type.IsZeroOrNull()) { + // ie not an array or null Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type; } else { work_line_->SetRegisterType<LockOp::kClear>(this, @@ -2592,7 +2593,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* Similar to the verification done for APUT */ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegA_31t()); /* array_type can be null if the reg type is Zero */ - if (!array_type.IsZero()) { + if (!array_type.IsZeroOrNull()) { if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; @@ -2632,7 +2633,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t()); const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t()); bool mismatch = false; - if (reg_type1.IsZero()) { // zero then integral or reference expected + if (reg_type1.IsZeroOrNull()) { // zero then integral or reference expected mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes(); } else if (reg_type1.IsReferenceTypes()) { // both references? mismatch = !reg_type2.IsReferenceTypes(); @@ -2717,7 +2718,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && cast_type.HasClass() && // Could be conflict type, make sure it has a class. !cast_type.GetClass()->IsInterface() && - (orig_type.IsZero() || + (orig_type.IsZeroOrNull() || orig_type.IsStrictlyAssignableFrom( cast_type.Merge(orig_type, ®_types_, this), this))) { RegisterLine* update_line = RegisterLine::Create(code_item_accessor_.RegistersSize(), @@ -3005,7 +3006,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; /* no null refs allowed (?) */ - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unable to initialize null ref"; break; } @@ -3082,7 +3083,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * interface or Object (see comments in RegType::JoinClass). */ const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at runtime) */ } else { if (this_type.IsUninitializedTypes()) { @@ -4081,7 +4082,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( const RegType& adjusted_type = is_init ? GetRegTypeCache()->FromUninitialized(actual_arg_type) : actual_arg_type; - if (method_type != METHOD_INTERFACE && !adjusted_type.IsZero()) { + if (method_type != METHOD_INTERFACE && !adjusted_type.IsZeroOrNull()) { const RegType* res_method_class; // Miranda methods have the declaring interface as their declaring class, not the abstract // class. It would be wrong to use this for the type check (interface type checks are @@ -4454,7 +4455,7 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) { const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at run time) */ return true; } else if (!this_type.IsNonZeroReferenceTypes()) { @@ -4573,7 +4574,7 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; return nullptr; } - if (!actual_arg_type.IsZero()) { + if (!actual_arg_type.IsZeroOrNull()) { mirror::Class* klass = res_method->GetDeclaringClass(); std::string temp; const RegType& res_method_class = @@ -4689,7 +4690,7 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { + if (array_type.IsZeroOrNull()) { have_pending_runtime_throw_failure_ = true; // Null array class; this code path will fail at runtime. Infer a merge-able type from the // instruction type. TODO: have a proper notion of bottom here. @@ -4804,7 +4805,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { + if (array_type.IsZeroOrNull()) { // Null array type; this code path will fail at runtime. // Still check that the given value matches the instruction's type. // Note: this is, as usual, complicated by the fact the the instruction isn't fully typed @@ -4926,7 +4927,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id DCHECK(self_->IsExceptionPending()); self_->ClearException(); return nullptr; - } else if (obj_type.IsZero()) { + } else if (obj_type.IsZeroOrNull()) { // Cannot infer and check type, however, access will cause null pointer exception. // Fall through into a few last soft failure checks below. } else if (!obj_type.IsReferenceTypes()) { diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 631c6bd7ef..f719782727 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -29,6 +29,8 @@ namespace art { namespace verifier { inline bool RegType::CanAccess(const RegType& other) const { + DCHECK(IsReferenceTypes()); + DCHECK(!IsNull()); if (Equals(other)) { return true; // Trivial accessibility. } else { @@ -45,9 +47,13 @@ inline bool RegType::CanAccess(const RegType& other) const { } inline bool RegType::CanAccessMember(ObjPtr<mirror::Class> klass, uint32_t access_flags) const { + DCHECK(IsReferenceTypes()); if ((access_flags & kAccPublic) != 0) { return true; } + if (IsNull()) { + return true; + } if (!IsUnresolvedTypes()) { return GetClass()->CanAccessMember(klass, access_flags); } else { @@ -92,7 +98,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, LOG(WARNING) << "RegType::AssignableFrom lhs is Conflict!"; return false; case AssignmentType::kReference: - if (rhs.IsZero()) { + if (rhs.IsZeroOrNull()) { return true; // All reference types can be assigned null. } else if (!rhs.IsReferenceTypes()) { return false; // Expect rhs to be a reference type. @@ -119,6 +125,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, return result; } else { // Unresolved types are only assignable for null and equality. + // Null cannot be the left-hand side. return false; } case AssignmentType::kNotAssignable: @@ -199,6 +206,11 @@ inline const UndefinedType* UndefinedType::GetInstance() { return instance_; } +inline const NullType* NullType::GetInstance() { + DCHECK(instance_ != nullptr); + return instance_; +} + inline void* RegType::operator new(size_t size, ScopedArenaAllocator* allocator) { return allocator->Alloc(size, kArenaAllocMisc); } diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 8df2e0f50b..309c374fa8 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -51,6 +51,7 @@ const LongHiType* LongHiType::instance_ = nullptr; const DoubleLoType* DoubleLoType::instance_ = nullptr; const DoubleHiType* DoubleHiType::instance_ = nullptr; const IntegerType* IntegerType::instance_ = nullptr; +const NullType* NullType::instance_ = nullptr; PrimitiveType::PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { @@ -581,6 +582,10 @@ static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { return a.IsConstantTypes() ? b : a; } +static const RegType& SelectNonConstant2(const RegType& a, const RegType& b) { + return a.IsConstantTypes() ? (b.IsZero() ? a : b) : a; +} + const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types, MethodVerifier* verifier) const { @@ -695,8 +700,8 @@ const RegType& RegType::Merge(const RegType& incoming_type, // special. They may only ever be merged with themselves (must be taken care of by the // caller of Merge(), see the DCHECK on entry). So mark any other merge as conflicting here. return conflict; - } else if (IsZero() || incoming_type.IsZero()) { - return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref + } else if (IsZeroOrNull() || incoming_type.IsZeroOrNull()) { + return SelectNonConstant2(*this, incoming_type); // 0 MERGE ref => ref } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) { return reg_types->JavaLangObject(false); // Object MERGE ref => Object } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) { @@ -965,6 +970,21 @@ bool RegType::CanAssignArray(const RegType& src, return cmp1.CanAssignArray(cmp2, reg_types, class_loader, verifier, soft_error); } +const NullType* NullType::CreateInstance(mirror::Class* klass, + const StringPiece& descriptor, + uint16_t cache_id) { + CHECK(instance_ == nullptr); + instance_ = new NullType(klass, descriptor, cache_id); + return instance_; +} + +void NullType::Destroy() { + if (NullType::instance_ != nullptr) { + delete instance_; + instance_ = nullptr; + } +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index a2085a3f09..9055849ca0 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -129,8 +129,12 @@ class RegType { virtual bool IsConstantShort() const { return false; } virtual bool IsOne() const { return false; } virtual bool IsZero() const { return false; } + virtual bool IsNull() const { return false; } bool IsReferenceTypes() const { - return IsNonZeroReferenceTypes() || IsZero(); + return IsNonZeroReferenceTypes() || IsZero() || IsNull(); + } + bool IsZeroOrNull() const { + return IsZero() || IsNull(); } virtual bool IsNonZeroReferenceTypes() const { return false; } bool IsCategory1Types() const { @@ -857,6 +861,46 @@ class ImpreciseConstHiType FINAL : public ConstantType { } }; +// Special "null" type that captures the semantics of null / bottom. +class NullType FINAL : public RegType { + public: + bool IsNull() const OVERRIDE { + return true; + } + + // Get the singleton Null instance. + static const NullType* GetInstance() PURE; + + // Create the singleton instance. + static const NullType* CreateInstance(mirror::Class* klass, + const StringPiece& descriptor, + uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_); + + static void Destroy(); + + std::string Dump() const OVERRIDE { + return "null"; + } + + AssignmentType GetAssignmentTypeImpl() const OVERRIDE { + return AssignmentType::kReference; + } + + bool IsConstantTypes() const OVERRIDE { + return true; + } + + private: + NullType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } + + static const NullType* instance_; +}; + // Common parent of all uninitialized types. Uninitialized types are created by // "new" dex // instructions and must be passed to a constructor. diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 197c97671e..61f34afdac 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -81,6 +81,9 @@ inline const UndefinedType& RegTypeCache::Undefined() { inline const ConflictType& RegTypeCache::Conflict() { return *ConflictType::GetInstance(); } +inline const NullType& RegTypeCache::Null() { + return *NullType::GetInstance(); +} inline const ImpreciseConstType& RegTypeCache::ByteConstant() { const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 4808381d5b..a3f08c8580 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -56,6 +56,7 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { // Note: this must have the same order as CreatePrimitiveAndSmallConstantTypes. entries_.push_back(UndefinedType::GetInstance()); entries_.push_back(ConflictType::GetInstance()); + entries_.push_back(NullType::GetInstance()); entries_.push_back(BooleanType::GetInstance()); entries_.push_back(ByteType::GetInstance()); entries_.push_back(ShortType::GetInstance()); @@ -307,6 +308,7 @@ void RegTypeCache::ShutDown() { FloatType::Destroy(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); + NullType::Destroy(); for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { const PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant]; delete type; @@ -354,6 +356,7 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { }; create_primitive_type_instance(TypeHelper<UndefinedType>("")); create_primitive_type_instance(TypeHelper<ConflictType>("")); + create_primitive_type_instance(TypeHelper<NullType>("")); create_primitive_type_instance(TypeHelper<BooleanType>("Z")); create_primitive_type_instance(TypeHelper<ByteType>("B")); create_primitive_type_instance(TypeHelper<ShortType>("S")); diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 054a2af458..52776766bc 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -49,6 +49,7 @@ class IntegerType; class LongHiType; class LongLoType; class MethodVerifier; +class NullType; class PreciseConstType; class PreciseReferenceType; class RegType; @@ -123,6 +124,7 @@ class RegTypeCache { const DoubleHiType& DoubleHi() REQUIRES_SHARED(Locks::mutator_lock_); const UndefinedType& Undefined() REQUIRES_SHARED(Locks::mutator_lock_); const ConflictType& Conflict(); + const NullType& Null(); const PreciseReferenceType& JavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_); const PreciseReferenceType& JavaLangString() REQUIRES_SHARED(Locks::mutator_lock_); @@ -180,7 +182,7 @@ class RegTypeCache { kMinSmallConstant + 1]; static constexpr size_t kNumPrimitivesAndSmallConstants = - 12 + (kMaxSmallConstant - kMinSmallConstant + 1); + 13 + (kMaxSmallConstant - kMinSmallConstant + 1); // Have the well known global primitives been created? static bool primitive_initialized_; diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 6edeecec02..15a38f3fd7 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -690,6 +690,8 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { // | | | | | | | // | #---------------#--------#---------#--------#---------# // | | + // | null + // | | // #--------------------------#----------------------------# // | // 0 @@ -707,6 +709,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& conflict = cache.Conflict(); const RegType& zero = cache.Zero(); + const RegType& null = cache.Null(); const RegType& int_type = cache.Integer(); const RegType& obj = cache.JavaLangObject(false); @@ -799,6 +802,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { plain_nonobj_arr_classes.begin(), plain_nonobj_arr_classes.end()); all_minus_uninit_conflict.push_back(&zero); + all_minus_uninit_conflict.push_back(&null); all_minus_uninit_conflict.push_back(&obj); std::vector<const RegType*> all_minus_uninit; @@ -853,10 +857,12 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ADD_EDGE(int_type, conflict); } + ADD_EDGE(zero, null); + // Unresolved. { - ADD_EDGE(zero, unresolved_a); - ADD_EDGE(zero, unresolved_b); + ADD_EDGE(null, unresolved_a); + ADD_EDGE(null, unresolved_b); ADD_EDGE(unresolved_a, unresolved_ab); ADD_EDGE(unresolved_b, unresolved_ab); @@ -887,7 +893,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { // Classes. { - ADD_EDGE(zero, integer); + ADD_EDGE(null, integer); ADD_EDGE(integer, number); ADD_EDGE(number, obj); } @@ -902,10 +908,10 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ADD_EDGE(char_arr, obj); ADD_EDGE(byte_arr, obj); - ADD_EDGE(zero, integer_arr); - ADD_EDGE(zero, number_arr_arr); - ADD_EDGE(zero, char_arr); - ADD_EDGE(zero, byte_arr); + ADD_EDGE(null, integer_arr); + ADD_EDGE(null, number_arr_arr); + ADD_EDGE(null, char_arr); + ADD_EDGE(null, byte_arr); } // Primitive. |