summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2024-12-03 15:22:28 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2024-12-05 14:18:24 +0000
commit22ee56acfb2dd6329cc2b594dfa1897d6edc8b78 (patch)
tree78691af83feb35c7e3cd9fee9784f2447bc3f635
parent68bb61936ea5d439f5eb63ddda9f3d665ce71e1d (diff)
verifier: Refactor `Is{,Strictly}AssignableFrom().
Move these functions to the `MethodVerifier`. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: I32346a670074a8ab48bdf0aa084e8885e8a644e1
-rw-r--r--runtime/verifier/method_verifier.cc105
-rw-r--r--runtime/verifier/method_verifier.h15
-rw-r--r--runtime/verifier/reg_type-inl.h87
-rw-r--r--runtime/verifier/reg_type.h19
4 files changed, 108 insertions, 118 deletions
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index e8a6de8991..8a6767ae36 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -743,7 +743,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier {
REQUIRES_SHARED(Locks::mutator_lock_) {
// Verify the src register type against the check type refining the type of the register
const RegType& src_type = work_line_->GetRegisterType(this, vsrc);
- if (UNLIKELY(!check_type.IsAssignableFrom(src_type, this))) {
+ if (UNLIKELY(!IsAssignableFrom(check_type, src_type))) {
enum VerifyError fail_type;
if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
// Hard fail if one of the types is primitive, since they are concretely known.
@@ -2501,7 +2501,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
// We really do expect a reference here.
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object returns a non-reference type "
<< reg_type;
- } else if (!return_type.IsAssignableFrom(reg_type, this)) {
+ } else if (!IsAssignableFrom(return_type, reg_type)) {
if (reg_type.IsUnresolvedTypes() || return_type.IsUnresolvedTypes()) {
Fail(VERIFY_ERROR_UNRESOLVED_TYPE_CHECK)
<< " can't resolve returned type '" << return_type << "' or '" << reg_type << "'";
@@ -2801,7 +2801,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
break;
case Instruction::THROW: {
const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x());
- if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type, this)) {
+ if (!IsAssignableFrom(reg_types_.JavaLangThrowable(), res_type)) {
if (res_type.IsUninitializedTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "thrown exception not initialized";
} else if (!res_type.IsReferenceTypes()) {
@@ -2954,8 +2954,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
cast_type.HasClass() && // Could be conflict type, make sure it has a class.
!cast_type.GetClass()->IsInterface() &&
!orig_type.IsZeroOrNull() &&
- orig_type.IsStrictlyAssignableFrom(
- cast_type.Merge(orig_type, &reg_types_, this), this)) {
+ IsStrictlyAssignableFrom(orig_type, cast_type.Merge(orig_type, &reg_types_, this))) {
RegisterLine* update_line = RegisterLine::Create(code_item_accessor_.RegistersSize(),
allocator_,
GetRegTypeCache());
@@ -3896,7 +3895,7 @@ bool MethodVerifier<kVerifierDebug>::HandleMoveException(const Instruction* inst
// Do access checks only on resolved exception classes.
const RegType& exception =
ResolveClass<CheckAccess::kOnResolvedClass>(iterator.GetHandlerTypeIndex());
- if (!reg_types_.JavaLangThrowable().IsAssignableFrom(exception, this)) {
+ if (!IsAssignableFrom(reg_types_.JavaLangThrowable(), exception)) {
DCHECK(!exception.IsUninitializedTypes()); // Comes from dex, shouldn't be uninit.
if (exception.IsUnresolvedTypes()) {
if (unresolved == nullptr) {
@@ -3915,10 +3914,9 @@ bool MethodVerifier<kVerifierDebug>::HandleMoveException(const Instruction* inst
// odd case, but nothing to do
} else {
common_super = &common_super->Merge(exception, &reg_types_, this);
- if (FailOrAbort(reg_types_.JavaLangThrowable().IsAssignableFrom(
- *common_super, this),
- "java.lang.Throwable is not assignable-from common_super at ",
- work_insn_idx_)) {
+ if (FailOrAbort(IsAssignableFrom(reg_types_.JavaLangThrowable(), *common_super),
+ "java.lang.Throwable is not assignable-from common_super at ",
+ work_insn_idx_)) {
break;
}
}
@@ -4163,7 +4161,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgsFromIterator(
res_method_class = &reg_types_.FromClass(klass);
}
}
- if (!res_method_class->IsAssignableFrom(adjusted_type, this)) {
+ if (!IsAssignableFrom(*res_method_class, adjusted_type)) {
Fail(adjusted_type.IsUnresolvedTypes()
? VERIFY_ERROR_UNRESOLVED_TYPE_CHECK
: VERIFY_ERROR_BAD_CLASS_HARD)
@@ -4335,7 +4333,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs(
<< "interface invoke-super";
VerifyInvocationArgsUnresolvedMethod(inst, method_type, is_range);
return nullptr;
- } else if (!reference_type.IsStrictlyAssignableFrom(GetDeclaringClass(), this)) {
+ } else if (!IsStrictlyAssignableFrom(reference_type, GetDeclaringClass())) {
Fail(VERIFY_ERROR_CLASS_CHANGE)
<< "invoke-super in " << mirror::Class::PrettyClass(GetDeclaringClass().GetClass())
<< " in method "
@@ -4362,7 +4360,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs(
VerifyInvocationArgsUnresolvedMethod(inst, method_type, is_range);
return nullptr;
}
- if (!reference_type.IsStrictlyAssignableFrom(GetDeclaringClass(), this) ||
+ if (!IsStrictlyAssignableFrom(reference_type, GetDeclaringClass()) ||
(res_method->GetMethodIndex() >= GetRegTypeClass(super)->GetVTableLength())) {
Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
<< dex_file_->PrettyMethod(dex_method_idx_)
@@ -4844,7 +4842,7 @@ ArtField* MethodVerifier<kVerifierDebug>::GetInstanceField(uint32_t vregB,
? klass_type
: reg_types_.FromClass(klass);
DCHECK(!obj_type.IsUninitializedTypes());
- if (!field_klass.IsAssignableFrom(obj_type, this)) {
+ if (!IsAssignableFrom(field_klass, obj_type)) {
// Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
// of C1. For resolution to occur the declared class of the field must be compatible with
// obj_type, we've discovered this wasn't so, so report the field didn't exist.
@@ -4933,7 +4931,7 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst
VerifyPrimitivePut(field_type, insn_type, vregA);
} else {
DCHECK(insn_type.IsJavaLangObject());
- if (!insn_type.IsAssignableFrom(field_type, this)) {
+ if (!IsAssignableFrom(insn_type, field_type)) {
DCHECK(!field_type.IsReferenceTypes());
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << ArtField::PrettyField(field)
<< " to be compatible with type '" << insn_type
@@ -4962,7 +4960,7 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst
}
} else {
DCHECK(insn_type.IsJavaLangObject());
- if (!insn_type.IsAssignableFrom(field_type, this)) {
+ if (!IsAssignableFrom(insn_type, field_type)) {
DCHECK(!field_type.IsReferenceTypes());
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << ArtField::PrettyField(field)
<< " to be compatible with type '" << insn_type
@@ -5537,5 +5535,80 @@ const RegType& MethodVerifier::GetInvocationThis(const Instruction* inst) {
return this_type;
}
+bool MethodVerifier::AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) const {
+ if (lhs.Equals(rhs)) {
+ return true;
+ }
+
+ RegType::Assignability assignable = RegType::AssignabilityFrom(lhs.GetKind(), rhs.GetKind());
+ DCHECK(assignable != RegType::Assignability::kInvalid)
+ << "Unexpected register type in IsAssignableFrom: '" << lhs << "' := '" << rhs << "'";
+ if (assignable == RegType::Assignability::kAssignable) {
+ return true;
+ } else if (assignable == RegType::Assignability::kNotAssignable) {
+ return false;
+ } else if (assignable == RegType::Assignability::kNarrowingConversion) {
+ // FIXME: The `MethodVerifier` is mostly doing a category check and avoiding
+ // assignability checks that would expose narrowing conversions. However, for
+ // the `return` instruction, it explicitly allows certain narrowing conversions
+ // and prohibits others by doing a modified assignability check. Without strict
+ // enforcement in all cases, this can compromise compiler optimizations that
+ // rely on knowing the range of the values. Bug: 270660613
+ return false;
+ } else {
+ DCHECK(assignable == RegType::Assignability::kReference);
+ DCHECK(lhs.IsNonZeroReferenceTypes());
+ DCHECK(rhs.IsNonZeroReferenceTypes());
+ DCHECK(!lhs.IsUninitializedTypes());
+ DCHECK(!rhs.IsUninitializedTypes());
+ DCHECK(!lhs.IsJavaLangObject());
+ if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
+ // If we're not strict allow assignment to any interface, see comment in ClassJoin.
+ return true;
+ } else if (lhs.IsJavaLangObjectArray()) {
+ return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
+ } else if (lhs.HasClass() && rhs.IsJavaLangObject()) {
+ return false; // Note: Non-strict check for interface `lhs` is handled above.
+ } else if (lhs.HasClass() && rhs.HasClass()) {
+ // Test assignability from the Class point-of-view.
+ bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
+ // Record assignability dependency. The `verifier` is null during unit tests and
+ // VerifiedMethod::GenerateSafeCastSet.
+ if (result) {
+ VerifierDeps::MaybeRecordAssignability(GetVerifierDeps(),
+ GetDexFile(),
+ GetClassDef(),
+ lhs.GetClass(),
+ rhs.GetClass());
+ }
+ return result;
+ } else {
+ // For unresolved types, we don't know if they are assignable, and the
+ // verifier will continue assuming they are. We need to record that.
+ //
+ // Note that if `rhs` is an interface type, `lhs` may be j.l.Object
+ // and if the assignability check is not strict, then this should be
+ // OK. However we don't encode strictness in the verifier deps, and
+ // such a situation will force a full verification.
+ VerifierDeps::MaybeRecordAssignability(GetVerifierDeps(),
+ GetDexFile(),
+ GetClassDef(),
+ lhs,
+ rhs);
+ // Unresolved types are only assignable for null and equality.
+ // Null cannot be the left-hand side.
+ return false;
+ }
+ }
+}
+
+inline bool MethodVerifier::IsAssignableFrom(const RegType& lhs, const RegType& rhs) const {
+ return AssignableFrom(lhs, rhs, false);
+}
+
+inline bool MethodVerifier::IsStrictlyAssignableFrom(const RegType& lhs, const RegType& rhs) const {
+ return AssignableFrom(lhs, rhs, true);
+}
+
} // namespace verifier
} // namespace art
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 2021f64525..13631850b7 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -261,6 +261,21 @@ class MethodVerifier {
const RegType& GetInvocationThis(const Instruction* inst)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Can a variable with type `lhs` be assigned a value with type `rhs`?
+ // Note: Object and interface types may always be assigned to one another, see
+ // comment on `ClassJoin()`.
+ bool IsAssignableFrom(const RegType& lhs, const RegType& rhs) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Can a variable with type `lhs` be assigned a value with type `rhs`?
+ // Variant of IsAssignableFrom that doesn't allow assignment to an interface from an Object.
+ bool IsStrictlyAssignableFrom(const RegType& lhs, const RegType& rhs) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Implementation helper for `IsAssignableFrom()` and `IsStrictlyAssignableFrom()`.
+ bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// For VerifierDepsTest. TODO: Refactor.
// Run verification on the method. Returns true if verification completes and false if the input
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index bc6c6c3ee9..770989c654 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -45,11 +45,11 @@ class RegTypeAssignabilityImpl final : RegType {
explicit constexpr RegTypeAssignabilityImpl(RegType::Kind kind)
: RegType("", /* unused cache id */ 0, kind) {}
- static constexpr Assignability AssignableFrom(RegType::Kind lhs_kind, RegType::Kind rhs_kind);
+ static constexpr Assignability AssignabilityFrom(RegType::Kind lhs_kind, RegType::Kind rhs_kind);
};
-constexpr RegType::Assignability RegTypeAssignabilityImpl::AssignableFrom(RegType::Kind lhs_kind,
- RegType::Kind rhs_kind) {
+constexpr RegType::Assignability RegTypeAssignabilityImpl::AssignabilityFrom(
+ RegType::Kind lhs_kind, RegType::Kind rhs_kind) {
RegTypeAssignabilityImpl lhs(lhs_kind);
RegTypeAssignabilityImpl rhs(rhs_kind);
auto maybe_narrowing_conversion = [&rhs]() constexpr {
@@ -112,7 +112,7 @@ inline RegType::Assignability RegType::AssignabilityFrom(Kind lhs, Kind rhs) {
AssignabilityTable result;
for (size_t lhs = 0u; lhs != kNumKinds; ++lhs) {
for (size_t rhs = 0u; rhs != kNumKinds; ++rhs) {
- result[lhs][rhs] = detail::RegTypeAssignabilityImpl::AssignableFrom(
+ result[lhs][rhs] = detail::RegTypeAssignabilityImpl::AssignabilityFrom(
enum_cast<RegType::Kind>(lhs), enum_cast<RegType::Kind>(rhs));
}
}
@@ -122,85 +122,6 @@ inline RegType::Assignability RegType::AssignabilityFrom(Kind lhs, Kind rhs) {
return kAssignabilityTable[lhs][rhs];
}
-inline bool RegType::AssignableFrom(const RegType& lhs,
- const RegType& rhs,
- bool strict,
- MethodVerifier* verifier) {
- if (lhs.Equals(rhs)) {
- return true;
- }
-
- Assignability assignable = AssignabilityFrom(lhs.GetKind(), rhs.GetKind());
- DCHECK(assignable != Assignability::kInvalid)
- << "Unexpected register type in IsAssignableFrom: '" << lhs << "' := '" << rhs << "'";
- if (assignable == Assignability::kAssignable) {
- return true;
- } else if (assignable == Assignability::kNotAssignable) {
- return false;
- } else if (assignable == Assignability::kNarrowingConversion) {
- // FIXME: The `MethodVerifier` is mostly doing a category check and avoiding
- // assignability checks that would expose narrowing conversions. However, for
- // the `return` instruction, it explicitly allows certain narrowing conversions
- // and prohibits others by doing a modified assignability check. Without strict
- // enforcement in all cases, this can compromise compiler optimizations that
- // rely on knowing the range of the values. Bug: 270660613
- return false;
- } else {
- DCHECK(assignable == Assignability::kReference);
- DCHECK(lhs.IsNonZeroReferenceTypes());
- DCHECK(rhs.IsNonZeroReferenceTypes());
- DCHECK(!lhs.IsUninitializedTypes());
- DCHECK(!rhs.IsUninitializedTypes());
- DCHECK(!lhs.IsJavaLangObject());
- if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
- // If we're not strict allow assignment to any interface, see comment in ClassJoin.
- return true;
- } else if (lhs.IsJavaLangObjectArray()) {
- return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
- } else if (lhs.HasClass() && rhs.IsJavaLangObject()) {
- return false; // Note: Non-strict check for interface `lhs` is handled above.
- } else if (lhs.HasClass() && rhs.HasClass()) {
- // Test assignability from the Class point-of-view.
- bool result = lhs.GetClass()->IsAssignableFrom(rhs.GetClass());
- // Record assignability dependency. The `verifier` is null during unit tests and
- // VerifiedMethod::GenerateSafeCastSet.
- if (verifier != nullptr && result) {
- VerifierDeps::MaybeRecordAssignability(verifier->GetVerifierDeps(),
- verifier->GetDexFile(),
- verifier->GetClassDef(),
- lhs.GetClass(),
- rhs.GetClass());
- }
- return result;
- } else {
- // For unresolved types, we don't know if they are assignable, and the
- // verifier will continue assuming they are. We need to record that.
- if (verifier != nullptr) {
- // Note that if `rhs` is an interface type, `lhs` may be j.l.Object
- // and if the assignability check is not strict, then this should be
- // OK. However we don't encode strictness in the verifier deps, and
- // such a situation will force a full verification.
- VerifierDeps::MaybeRecordAssignability(verifier->GetVerifierDeps(),
- verifier->GetDexFile(),
- verifier->GetClassDef(),
- lhs,
- rhs);
- }
- // Unresolved types are only assignable for null and equality.
- // Null cannot be the left-hand side.
- return false;
- }
- }
-}
-
-inline bool RegType::IsAssignableFrom(const RegType& src, MethodVerifier* verifier) const {
- return AssignableFrom(*this, src, false, verifier);
-}
-
-inline bool RegType::IsStrictlyAssignableFrom(const RegType& src, MethodVerifier* verifier) const {
- return AssignableFrom(*this, src, true, verifier);
-}
-
inline void* RegType::operator new(size_t size, ArenaAllocator* allocator) {
return allocator->Alloc(size, kArenaAllocMisc);
}
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 56c0485bf4..a8f31c523c 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -240,19 +240,6 @@ class RegType {
ALWAYS_INLINE static inline Assignability AssignabilityFrom(Kind lhs, Kind rhs);
- // Can this type be assigned by src?
- // Note: Object and interface types may always be assigned to one another, see
- // comment on
- // ClassJoin.
- bool IsAssignableFrom(const RegType& src, MethodVerifier* verifier) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't
- // allow assignment to
- // an interface from an Object.
- bool IsStrictlyAssignableFrom(const RegType& src, MethodVerifier* verifier) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Are these RegTypes the same?
bool Equals(const RegType& other) const { return GetId() == other.GetId(); }
@@ -300,12 +287,6 @@ class RegType {
friend class RegTypeCache;
private:
- static bool AssignableFrom(const RegType& lhs,
- const RegType& rhs,
- bool strict,
- MethodVerifier* verifier)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
DISALLOW_COPY_AND_ASSIGN(RegType);
};