/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_ #define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_ #include "local_reference_table.h" #include "android-base/stringprintf.h" #include "base/casts.h" #include "gc_root-inl.h" #include "obj_ptr-inl.h" #include "mirror/object_reference.h" #include "verify_object.h" namespace art HIDDEN { namespace jni { inline void LrtEntry::SetReference(ObjPtr ref) { root_ = GcRoot( mirror::CompressedReference::FromMirrorPtr(ref.Ptr())); DCHECK(!IsFree()); DCHECK(!IsSerialNumber()); } inline ObjPtr LrtEntry::GetReference() { DCHECK(!IsFree()); DCHECK(!IsSerialNumber()); DCHECK(!IsNull()); // Local references do not need read barriers. They are marked during the thread root flip. return root_.Read(); } inline void LrtEntry::SetNextFree(uint32_t next_free) { SetVRegValue(NextFreeField::Update(next_free, 1u << kFlagFree)); DCHECK(IsFree()); DCHECK(!IsSerialNumber()); } inline void LrtEntry::SetSerialNumber(uint32_t serial_number) { SetVRegValue(SerialNumberField::Update(serial_number, 1u << kFlagSerialNumber)); DCHECK(!IsFree()); DCHECK(IsSerialNumber()); } inline void LrtEntry::SetVRegValue(uint32_t value) { root_ = GcRoot( mirror::CompressedReference::FromVRegValue(value)); } inline uint32_t LocalReferenceTable::GetReferenceEntryIndex(IndirectRef iref) const { DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(iref), kLocal); LrtEntry* entry = ToLrtEntry(iref); if (LIKELY(small_table_ != nullptr)) { DCHECK(tables_.empty()); if (!std::less()(entry, small_table_) && std::less()(entry, small_table_ + kSmallLrtEntries)) { return dchecked_integral_cast(entry - small_table_); } } else { for (size_t i = 0, size = tables_.size(); i != size; ++i) { LrtEntry* table = tables_[i]; size_t table_size = GetTableSize(i); if (!std::less()(entry, table) && std::less()(entry, table + table_size)) { return dchecked_integral_cast(i != 0u ? table_size : 0u) + dchecked_integral_cast(entry - table); } } } return std::numeric_limits::max(); } inline bool LocalReferenceTable::IsValidReference(IndirectRef iref, /*out*/std::string* error_msg) const { uint32_t entry_index = GetReferenceEntryIndex(iref); if (UNLIKELY(entry_index == std::numeric_limits::max())) { *error_msg = android::base::StringPrintf("reference outside the table: %p", iref); return false; } if (UNLIKELY(entry_index >= segment_state_.top_index)) { *error_msg = android::base::StringPrintf("popped reference at index %u in a table of size %u", entry_index, segment_state_.top_index); return false; } LrtEntry* entry = ToLrtEntry(iref); LrtEntry* serial_number_entry = GetCheckJniSerialNumberEntry(entry); if (serial_number_entry->IsSerialNumber()) { // This reference was created with CheckJNI enabled. uint32_t expected_serial_number = serial_number_entry->GetSerialNumber(); uint32_t serial_number = entry - serial_number_entry; DCHECK_LT(serial_number, kCheckJniEntriesPerReference); if (serial_number != expected_serial_number || serial_number == 0u) { *error_msg = android::base::StringPrintf( "reference at index %u with bad serial number %u v. %u (valid 1 - %u)", entry_index, serial_number, expected_serial_number, dchecked_integral_cast(kCheckJniEntriesPerReference - 1u)); return false; } } if (UNLIKELY(entry->IsFree())) { *error_msg = android::base::StringPrintf("deleted reference at index %u", entry_index); return false; } if (UNLIKELY(entry->IsNull())) { // This should never really happen and may indicate memory coruption. *error_msg = android::base::StringPrintf("null reference at index %u", entry_index); return false; } return true; } inline void LocalReferenceTable::DCheckValidReference(IndirectRef iref) const { // If CheckJNI is performing the checks, we should not reach this point with an invalid // reference with the exception of gtests that intercept the CheckJNI abort and proceed // to decode the reference anyway and we do not want to abort again in this case. if (kIsDebugBuild && !IsCheckJniEnabled()) { std::string error_msg; CHECK(IsValidReference(iref, &error_msg)) << error_msg; } } inline ObjPtr LocalReferenceTable::Get(IndirectRef iref) const { DCheckValidReference(iref); return ToLrtEntry(iref)->GetReference(); } inline void LocalReferenceTable::Update(IndirectRef iref, ObjPtr obj) { DCheckValidReference(iref); ToLrtEntry(iref)->SetReference(obj); } } // namespace jni } // namespace art #endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_