Check interface assignability strictly for check-cast elision.
Objects are assignable to interfaces in the verifier as described in the long
RegType::ClassJoin comment. Using IsAssignableFrom therefore brought about
a bug where all check-casts where the cast type was an interface were being
elided. Introduce a new strict is assignable from routine that follows the
runtime rather than the verifier is assignable from behavior.
Change-Id: Iac30f71ce9116f2ef16c22acd73eea1c19c79d89
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 1bb163a..8ca5b82 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -3352,7 +3352,7 @@
RegisterLine* line = reg_table_.GetLine(dex_pc);
const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- if (cast_type.IsAssignableFrom(reg_type)) {
+ if (cast_type.IsStrictlyAssignableFrom(reg_type)) {
if (mscs.get() == NULL) {
mscs.reset(new MethodSafeCastSet());
}
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index 8111db6..1c61a29 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -708,54 +708,62 @@
: ConstantType(constat, cache_id) {
}
-bool RegType::IsAssignableFrom(const RegType& src) const {
- if (Equals(src)) {
+static bool AssignableFrom(const RegType& lhs, const RegType& rhs, bool strict)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (lhs.Equals(rhs)) {
return true;
} else {
- if (IsBoolean()) {
- return src.IsBooleanTypes();
- } else if (IsByte()) {
- return src.IsByteTypes();
- } else if (IsShort()) {
- return src.IsShortTypes();
- } else if (IsChar()) {
- return src.IsCharTypes();
- } else if (IsInteger()) {
- return src.IsIntegralTypes();
- } else if (IsFloat()) {
- return src.IsFloatTypes();
- } else if (IsLongLo()) {
- return src.IsLongTypes();
- } else if (IsDoubleLo()) {
- return src.IsDoubleTypes();
+ if (lhs.IsBoolean()) {
+ return rhs.IsBooleanTypes();
+ } else if (lhs.IsByte()) {
+ return rhs.IsByteTypes();
+ } else if (lhs.IsShort()) {
+ return rhs.IsShortTypes();
+ } else if (lhs.IsChar()) {
+ return rhs.IsCharTypes();
+ } else if (lhs.IsInteger()) {
+ return rhs.IsIntegralTypes();
+ } else if (lhs.IsFloat()) {
+ return rhs.IsFloatTypes();
+ } else if (lhs.IsLongLo()) {
+ return rhs.IsLongTypes();
+ } else if (lhs.IsDoubleLo()) {
+ return rhs.IsDoubleTypes();
} else {
- if (!IsReferenceTypes()) {
- LOG(FATAL) << "Unexpected register type in 4bleFrom: '" << src << "'";
- }
- if (src.IsZero()) {
- return true; // all reference types can be assigned null
- } else if (!src.IsReferenceTypes()) {
- return false; // expect src to be a reference type
- } else if (IsJavaLangObject()) {
- return true; // all reference types can be assigned to Object
- } else if (!IsUnresolvedTypes() && GetClass()->IsInterface()) {
- return true; // We allow assignment to any interface, see comment in ClassJoin
- } else if (IsJavaLangObjectArray()) {
- return src.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
- } else if (!IsUnresolvedTypes() && !src.IsUnresolvedTypes() &&
- GetClass()->IsAssignableFrom(src.GetClass())) {
- // We're assignable from the Class point-of-view
+ CHECK(lhs.IsReferenceTypes())
+ << "Unexpected register type in IsAssignableFrom: '"
+ << lhs << "' := '" << rhs << "'";
+ if (rhs.IsZero()) {
+ return true; // All reference types can be assigned null.
+ } else if (!rhs.IsReferenceTypes()) {
+ return false; // Expect rhs to be a reference type.
+ } else if (lhs.IsJavaLangObject()) {
+ return true; // All reference types can be assigned to Object.
+ } else 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 (IsUnresolvedTypes()) {
- // Unresolved types are only assignable for null and equality.
- return src.IsZero();
+ } else if (lhs.IsJavaLangObjectArray()) {
+ return rhs.IsObjectArrayTypes(); // All reference arrays may be assigned to Object[]
+ } else if (lhs.HasClass() && rhs.HasClass() &&
+ lhs.GetClass()->IsAssignableFrom(rhs.GetClass())) {
+ // We're assignable from the Class point-of-view.
+ return true;
} else {
+ // Unresolved types are only assignable for null and equality.
return false;
}
}
}
}
+bool RegType::IsAssignableFrom(const RegType& src) const {
+ return AssignableFrom(*this, src, false);
+}
+
+bool RegType::IsStrictlyAssignableFrom(const RegType& src) const {
+ return AssignableFrom(*this, src, true);
+}
+
int32_t ConstantType::ConstantValue() const {
DCHECK(IsConstantTypes());
return constant_;
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 424b071..9ac0eca 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -230,7 +230,14 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Can this type be assigned by src?
- bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Note: Object and interface types may always be assigned to one another, see comment on
+ // ClassJoin.
+ bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(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) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Are these RegTypes the same?
bool Equals(const RegType& other) const {
@@ -241,23 +248,22 @@
virtual const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- /*
- * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
- * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
- * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
- * is the deepest (lowest upper bound) parent of S and T).
- *
- * This operation applies for regular classes and arrays, however, for interface types there needn't
- * be a partial ordering on the types. We could solve the problem of a lack of a partial order by
- * introducing sets of types, however, the only operation permissible on an interface is
- * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
- * types until an invoke-interface call on the interface typed reference at runtime and allow
- * the perversion of Object being assignable to an interface type (note, however, that we don't
- * allow assignment of Object or Interface to any concrete class and are therefore type safe).
- *
- * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
- */
-
+ /*
+ * A basic Join operation on classes. For a pair of types S and T the Join, written S v T = J, is
+ * S <: J, T <: J and for-all U such that S <: U, T <: U then J <: U. That is J is the parent of
+ * S and T such that there isn't a parent of both S and T that isn't also the parent of J (ie J
+ * is the deepest (lowest upper bound) parent of S and T).
+ *
+ * This operation applies for regular classes and arrays, however, for interface types there
+ * needn't be a partial ordering on the types. We could solve the problem of a lack of a partial
+ * order by introducing sets of types, however, the only operation permissible on an interface is
+ * invoke-interface. In the tradition of Java verifiers [1] we defer the verification of interface
+ * types until an invoke-interface call on the interface typed reference at runtime and allow
+ * the perversion of Object being assignable to an interface type (note, however, that we don't
+ * allow assignment of Object or Interface to any concrete class and are therefore type safe).
+ *
+ * [1] Java bytecode verification: algorithms and formalizations, Xavier Leroy
+ */
static mirror::Class* ClassJoin(mirror::Class* s, mirror::Class* t)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);