ART: Redo verification on field resolution failure
When compile-time verifying a field PUT and the field cannot be resolved,
the verification must be redone at runtime to ensure the field is not
final. Post an ACCESS_FIELD error.
An example is
dex file A:
class A {
{
B b = new B();
B.final_field = 12345; // illegally modify final-after-new
}
}
dex file B:
class B {
final int final_field = 0;
}
when A is compiled without B.
Bug: 34966607
Bug: 64681719
Test: m test-art-host
Test: cts-tradefed run commandAndExit cts --m vm-tests-tf
Change-Id: Ibc14b003288f7acf3c865fcdef54a6d9ed4ac867
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 312d8df..cf4a6e0 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4994,6 +4994,31 @@
DCHECK(!can_load_classes_ || self_->IsExceptionPending());
self_->ClearException();
}
+ } else {
+ // If we don't have the field (it seems we failed resolution) and this is a PUT, we need to
+ // redo verification at runtime as the field may be final, unless the field id shows it's in
+ // the same class.
+ //
+ // For simplicity, it is OK to not distinguish compile-time vs runtime, and post this an
+ // ACCESS_FIELD failure at runtime. This has the same effect as NO_FIELD - punting the class
+ // to the access-checks interpreter.
+ //
+ // Note: see b/34966607. This and above may be changed in the future.
+ if (kAccType == FieldAccessType::kAccPut) {
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ const char* field_class_descriptor = dex_file_->GetFieldDeclaringClassDescriptor(field_id);
+ const RegType* field_class_type = ®_types_.FromDescriptor(GetClassLoader(),
+ field_class_descriptor,
+ false);
+ if (!field_class_type->Equals(GetDeclaringClass())) {
+ Fail(VERIFY_ERROR_ACCESS_FIELD) << "could not check field put for final field modify of "
+ << field_class_descriptor
+ << "."
+ << dex_file_->GetFieldName(field_id)
+ << " from other class "
+ << GetDeclaringClass();
+ }
+ }
}
if (field_type == nullptr) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);