summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/execution_subgraph.cc13
-rw-r--r--libdexfile/dex/dex_file.h2
-rw-r--r--runtime/oat_file.cc110
-rw-r--r--runtime/vdex_file.cc3
-rw-r--r--runtime/vdex_file.h5
-rw-r--r--test/826-infinite-loop/expected-stderr.txt0
-rw-r--r--test/826-infinite-loop/expected-stdout.txt0
-rw-r--r--test/826-infinite-loop/info.txt2
-rw-r--r--test/826-infinite-loop/src/Main.java26
9 files changed, 137 insertions, 24 deletions
diff --git a/compiler/optimizing/execution_subgraph.cc b/compiler/optimizing/execution_subgraph.cc
index 6d105668c0..66fdfcda5b 100644
--- a/compiler/optimizing/execution_subgraph.cc
+++ b/compiler/optimizing/execution_subgraph.cc
@@ -86,12 +86,6 @@ void ExecutionSubgraph::Prune() {
ScopedArenaVector<std::bitset<kMaxFilterableSuccessors>> results(
graph_->GetBlocks().size(), temporaries.Adapter(kArenaAllocLSA));
unreachable_blocks_.ClearAllBits();
- // TODO We should support infinite loops as well.
- if (UNLIKELY(graph_->GetExitBlock() == nullptr)) {
- // Infinite loop
- valid_ = false;
- return;
- }
// Fills up the 'results' map with what we need to add to update
// allowed_successors in order to prune sink nodes.
bool start_reaches_end = false;
@@ -170,8 +164,11 @@ void ExecutionSubgraph::Prune() {
<< "current path size: " << current_path.size()
<< " cur_block id: " << cur_block->GetBlockId() << " entry id "
<< graph_->GetEntryBlock()->GetBlockId();
- DCHECK(!visiting.IsBitSet(id))
- << "Somehow ended up in a loop! This should have been caught before now! " << id;
+ if (visiting.IsBitSet(id)) {
+ // TODO We should support infinite loops as well.
+ start_reaches_end = false;
+ break;
+ }
std::bitset<kMaxFilterableSuccessors>& result = results[id];
if (cur_block == graph_->GetExitBlock()) {
start_reaches_end = true;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index 5363b00c24..e3027fc95f 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -738,7 +738,7 @@ class DexFile {
}
// Used by oat writer.
- void SetOatDexFile(OatDexFile* oat_dex_file) const {
+ void SetOatDexFile(const OatDexFile* oat_dex_file) const {
oat_dex_file_ = oat_dex_file;
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 8f653c2282..14e7a1b60e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -168,7 +168,7 @@ class OatFileBase : public OatFile {
bool Setup(int zip_fd, ArrayRef<const std::string> dex_filenames, std::string* error_msg);
- void Setup(const std::vector<const DexFile*>& dex_files);
+ bool Setup(const std::vector<const DexFile*>& dex_files, std::string* error_msg);
// Setters exposed for ElfOatFile.
@@ -476,18 +476,71 @@ static bool ReadIndexBssMapping(OatFile* oat_file,
return true;
}
-void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
+static bool ComputeAndCheckTypeLookupTableData(const DexFile::Header& header,
+ const uint8_t* type_lookup_table_start,
+ const VdexFile* vdex_file,
+ const uint8_t** type_lookup_table_data,
+ std::string* error_msg) {
+ if (type_lookup_table_start == nullptr ||
+ reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0] == 0) {
+ *type_lookup_table_data = nullptr;
+ return true;
+ }
+
+ *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
+ size_t found_size = reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0];
+ if (UNLIKELY(found_size != expected_table_size)) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' unexpected type lookup table size: found %zu, expected %zu",
+ vdex_file->GetName().c_str(),
+ found_size,
+ expected_table_size);
+ return false;
+ }
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table pointer %p not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_data,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data + expected_table_size - 1))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found overflowing type lookup table %p not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_data + expected_table_size,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+ if (UNLIKELY(!IsAligned<4>(type_lookup_table_start))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table alignment %p",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_start);
+ return false;
+ }
+ return true;
+}
+
+bool OatFileBase::Setup(const std::vector<const DexFile*>& dex_files, std::string* error_msg) {
uint32_t i = 0;
const uint8_t* type_lookup_table_start = nullptr;
for (const DexFile* dex_file : dex_files) {
- type_lookup_table_start = vdex_->GetNextTypeLookupTableData(type_lookup_table_start, i++);
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
+ type_lookup_table_start = vdex_->GetNextTypeLookupTableData(type_lookup_table_start, i++);
const uint8_t* type_lookup_table_data = nullptr;
- if (type_lookup_table_start != nullptr &&
- (reinterpret_cast<uint32_t*>(type_lookup_table_start[0]) != 0)) {
- type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (!ComputeAndCheckTypeLookupTableData(dex_file->GetHeader(),
+ type_lookup_table_start,
+ vdex_.get(),
+ &type_lookup_table_data,
+ error_msg)) {
+ return false;
}
// Create an OatDexFile and add it to the owning container.
OatDexFile* oat_dex_file = new OatDexFile(
@@ -497,7 +550,6 @@ void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
dex_location,
canonical_location,
type_lookup_table_data);
- dex_file->SetOatDexFile(oat_dex_file);
oat_dex_files_storage_.push_back(oat_dex_file);
// Add the location and canonical location (if different) to the oat_dex_files_ table.
@@ -508,6 +560,11 @@ void OatFileBase::Setup(const std::vector<const DexFile*>& dex_files) {
oat_dex_files_.Put(canonical_key, oat_dex_file);
}
}
+ // Now that we've created all the OatDexFile, update the dex files.
+ for (i = 0; i < dex_files.size(); ++i) {
+ dex_files[i]->SetOatDexFile(oat_dex_files_storage_[i]);
+ }
+ return true;
}
bool OatFileBase::Setup(int zip_fd,
@@ -1583,7 +1640,11 @@ class OatFileBackedByVdex final : public OatFileBase {
oat_file->SetVdex(vdex_file.release());
oat_file->SetupHeader(dex_files.size());
// Initialize OatDexFiles.
- oat_file->Setup(dex_files);
+ std::string error_msg;
+ if (!oat_file->Setup(dex_files, &error_msg)) {
+ LOG(WARNING) << "Could not create in-memory vdex file: " << error_msg;
+ return nullptr;
+ }
return oat_file.release();
}
@@ -1601,6 +1662,25 @@ class OatFileBackedByVdex final : public OatFileBase {
for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr, i);
dex_file_start != nullptr;
dex_file_start = vdex_file->GetNextDexFileData(dex_file_start, ++i)) {
+ const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid dex file pointer %p not in [%p, %p]",
+ dex_location.c_str(),
+ dex_file_start,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return nullptr;
+ }
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start + header->file_size_ - 1))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found overflowing dex file %p not in [%p, %p]",
+ dex_location.c_str(),
+ dex_file_start + header->file_size_,
+ vdex_file->Begin(),
+ vdex_file->End());
+ return nullptr;
+ }
if (UNLIKELY(!DexFileLoader::IsVersionAndMagicValid(dex_file_start))) {
*error_msg =
StringPrintf("In vdex file '%s' found dex file with invalid dex file version",
@@ -1612,10 +1692,14 @@ class OatFileBackedByVdex final : public OatFileBase {
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(location.c_str());
type_lookup_table_start = vdex_file->GetNextTypeLookupTableData(type_lookup_table_start, i);
const uint8_t* type_lookup_table_data = nullptr;
- if (type_lookup_table_start != nullptr &&
- (reinterpret_cast<uint32_t*>(type_lookup_table_start[0]) != 0)) {
- type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (!ComputeAndCheckTypeLookupTableData(*header,
+ type_lookup_table_start,
+ vdex_file,
+ &type_lookup_table_data,
+ error_msg)) {
+ return nullptr;
}
+
OatDexFile* oat_dex_file = new OatDexFile(oat_file.get(),
dex_file_start,
vdex_file->GetLocationChecksum(i),
@@ -1656,7 +1740,9 @@ class OatFileBackedByVdex final : public OatFileBase {
return nullptr;
}
oat_file->SetupHeader(oat_file->external_dex_files_.size());
- oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_));
+ if (!oat_file->Setup(MakeNonOwningPointerVector(oat_file->external_dex_files_), error_msg)) {
+ return nullptr;
+ }
}
return oat_file.release();
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 29efd4016f..967167961c 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -192,7 +192,8 @@ const uint8_t* VdexFile::GetNextTypeLookupTableData(const uint8_t* cursor,
} else {
const uint8_t* data = cursor + sizeof(uint32_t) + reinterpret_cast<const uint32_t*>(cursor)[0];
// TypeLookupTables are required to be 4 byte aligned. the OatWriter makes sure they are.
- CHECK_ALIGNED(data, 4);
+ // We don't check this here to be defensive against corrupted vdex files.
+ // Callers should check the returned value matches their expectations.
return data;
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index eb8b81742b..a66ff88fb2 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -157,8 +157,6 @@ class VdexFile {
return size;
}
- bool IsDexSectionValid() const;
-
bool HasDexSection() const {
return GetSectionHeader(VdexSection::kDexFileSection).section_size != 0u;
}
@@ -251,6 +249,9 @@ class VdexFile {
const uint8_t* Begin() const { return mmap_.Begin(); }
const uint8_t* End() const { return mmap_.End(); }
size_t Size() const { return mmap_.Size(); }
+ bool Contains(const uint8_t* pointer) const {
+ return pointer >= Begin() && pointer < End();
+ }
const VdexFileHeader& GetVdexFileHeader() const {
return *reinterpret_cast<const VdexFileHeader*>(Begin());
diff --git a/test/826-infinite-loop/expected-stderr.txt b/test/826-infinite-loop/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/826-infinite-loop/expected-stderr.txt
diff --git a/test/826-infinite-loop/expected-stdout.txt b/test/826-infinite-loop/expected-stdout.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/826-infinite-loop/expected-stdout.txt
diff --git a/test/826-infinite-loop/info.txt b/test/826-infinite-loop/info.txt
new file mode 100644
index 0000000000..13a89d8ca2
--- /dev/null
+++ b/test/826-infinite-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for partial escape elimination, which used to crash when
+visiting an infinite loop.
diff --git a/test/826-infinite-loop/src/Main.java b/test/826-infinite-loop/src/Main.java
new file mode 100644
index 0000000000..85bcb28baf
--- /dev/null
+++ b/test/826-infinite-loop/src/Main.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+final class Main {
+ public static void main(String[] args) {
+ Object o = new Object();
+ if (args.length == 0) {
+ while (true) {
+ System.out.println(new Object());
+ }
+ }
+ }
+}