• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cstdint>
18 #include <filesystem>
19 #include <unordered_set>
20 
21 #include "android-base/file.h"
22 #include "android-base/macros.h"
23 #include "common_runtime_test.h"
24 #include "dex/class_accessor-inl.h"
25 #include "dex/dex_file_verifier.h"
26 #include "dex/standard_dex_file.h"
27 #include "gtest/gtest.h"
28 #include "handle_scope-inl.h"
29 #include "jni/java_vm_ext.h"
30 #include "verifier/class_verifier.h"
31 #include "ziparchive/zip_archive.h"
32 
33 namespace art {
34 // Global variable to count how many DEX files passed DEX file verification and they were
35 // registered, since these are the cases for which we would be running the GC.
36 int skipped_gc_iterations = 0;
37 // Global variable to call the GC once every maximum number of iterations.
38 // TODO: These values were obtained from local experimenting. They can be changed after
39 // further investigation.
40 static constexpr int kMaxSkipGCIterations = 100;
41 
42 // A class to be friends with ClassLinker and access the internal FindDexCacheDataLocked method.
43 // TODO: Deduplicate this since it is the same with tools/fuzzer/libart_verify_classes_fuzzer.cc.
44 class VerifyClassesFuzzerCorpusTestHelper {
45  public:
GetDexCacheData(Runtime * runtime,const DexFile * dex_file)46   static const ClassLinker::DexCacheData* GetDexCacheData(Runtime* runtime, const DexFile* dex_file)
47       REQUIRES_SHARED(Locks::mutator_lock_) {
48     Thread* self = Thread::Current();
49     ReaderMutexLock mu(self, *Locks::dex_lock_);
50     ClassLinker* class_linker = runtime->GetClassLinker();
51     const ClassLinker::DexCacheData* cached_data = class_linker->FindDexCacheDataLocked(*dex_file);
52     return cached_data;
53   }
54 };
55 
56 // Manages the ZipArchiveHandle liveness.
57 class ZipArchiveHandleScope {
58  public:
ZipArchiveHandleScope(ZipArchiveHandle * handle)59   explicit ZipArchiveHandleScope(ZipArchiveHandle* handle) : handle_(handle) {}
~ZipArchiveHandleScope()60   ~ZipArchiveHandleScope() { CloseArchive(*(handle_.release())); }
61 
62  private:
63   std::unique_ptr<ZipArchiveHandle> handle_;
64 };
65 
66 class FuzzerCorpusTest : public CommonRuntimeTest {
67  public:
DexFileVerification(const uint8_t * data,size_t size,const std::string & name,bool expected_success)68   static void DexFileVerification(const uint8_t* data,
69                                   size_t size,
70                                   const std::string& name,
71                                   bool expected_success) {
72     // Do not verify the checksum as we only care about the DEX file contents,
73     // and know that the checksum would probably be erroneous (i.e. random).
74     constexpr bool kVerify = false;
75 
76     auto container = std::make_shared<MemoryDexFileContainer>(data, size);
77     StandardDexFile dex_file(data,
78                              /*location=*/name,
79                              /*location_checksum=*/0,
80                              /*oat_dex_file=*/nullptr,
81                              container);
82 
83     std::string error_msg;
84     bool is_valid_dex_file =
85         dex::Verify(&dex_file, dex_file.GetLocation().c_str(), kVerify, &error_msg);
86     ASSERT_EQ(is_valid_dex_file, expected_success) << " Failed for " << name;
87   }
88 
ClassVerification(const uint8_t * data,size_t size,const std::string & name,bool expected_success)89   static void ClassVerification(const uint8_t* data,
90                                 size_t size,
91                                 const std::string& name,
92                                 bool expected_success) {
93     // Do not verify the checksum as we only care about the DEX file contents,
94     // and know that the checksum would probably be erroneous (i.e. random)
95     constexpr bool kVerify = false;
96     bool passed_class_verification = true;
97 
98     auto container = std::make_shared<MemoryDexFileContainer>(data, size);
99     StandardDexFile dex_file(data,
100                              /*location=*/name,
101                              /*location_checksum=*/0,
102                              /*oat_dex_file=*/nullptr,
103                              container);
104 
105     std::string error_msg;
106     const bool success_dex =
107         dex::Verify(&dex_file, dex_file.GetLocation().c_str(), kVerify, &error_msg);
108     ASSERT_EQ(success_dex, true) << " Failed for " << name;
109 
110     Runtime* runtime = Runtime::Current();
111     CHECK(runtime != nullptr);
112 
113     ScopedObjectAccess soa(Thread::Current());
114     ClassLinker* class_linker = runtime->GetClassLinker();
115     jobject class_loader = RegisterDexFileAndGetClassLoader(runtime, &dex_file);
116 
117     // Scope for the handles
118     {
119       art::StackHandleScope<4> scope(soa.Self());
120       art::Handle<art::mirror::ClassLoader> h_loader =
121           scope.NewHandle(soa.Decode<art::mirror::ClassLoader>(class_loader));
122       art::MutableHandle<art::mirror::Class> h_klass(scope.NewHandle<art::mirror::Class>(nullptr));
123       art::MutableHandle<art::mirror::DexCache> h_dex_cache(
124           scope.NewHandle<art::mirror::DexCache>(nullptr));
125       art::MutableHandle<art::mirror::ClassLoader> h_dex_cache_class_loader =
126           scope.NewHandle(h_loader.Get());
127 
128       for (art::ClassAccessor accessor : dex_file.GetClasses()) {
129         h_klass.Assign(
130             class_linker->FindClass(soa.Self(), dex_file, accessor.GetClassIdx(), h_loader));
131         // Ignore classes that couldn't be loaded since we are looking for crashes during
132         // class/method verification.
133         if (h_klass == nullptr || h_klass->IsErroneous()) {
134           // Treat as failure to pass verification
135           passed_class_verification = false;
136           soa.Self()->ClearException();
137           continue;
138         }
139         h_dex_cache.Assign(h_klass->GetDexCache());
140 
141         // The class loader from the class's dex cache is different from the dex file's class loader
142         // for boot image classes e.g. java.util.AbstractCollection.
143         h_dex_cache_class_loader.Assign(h_klass->GetDexCache()->GetClassLoader());
144         verifier::FailureKind failure =
145             verifier::ClassVerifier::VerifyClass(soa.Self(),
146                                                  /* verifier_deps= */ nullptr,
147                                                  h_dex_cache->GetDexFile(),
148                                                  h_klass,
149                                                  h_dex_cache,
150                                                  h_dex_cache_class_loader,
151                                                  *h_klass->GetClassDef(),
152                                                  runtime->GetCompilerCallbacks(),
153                                                  verifier::HardFailLogMode::kLogWarning,
154                                                  /* api_level= */ 0,
155                                                  &error_msg);
156         if (failure != verifier::FailureKind::kNoFailure) {
157           passed_class_verification = false;
158         }
159       }
160     }
161     skipped_gc_iterations++;
162 
163     // Delete weak root to the DexCache before removing a DEX file from the cache. This is usually
164     // handled by the GC, but since we are not calling it every iteration, we need to delete them
165     // manually.
166     const ClassLinker::DexCacheData* dex_cache_data =
167         VerifyClassesFuzzerCorpusTestHelper::GetDexCacheData(runtime, &dex_file);
168     soa.Env()->GetVm()->DeleteWeakGlobalRef(soa.Self(), dex_cache_data->weak_root);
169 
170     class_linker->RemoveDexFromCaches(dex_file);
171 
172     // Delete global ref and unload class loader to free RAM.
173     soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), class_loader);
174 
175     if (skipped_gc_iterations == kMaxSkipGCIterations) {
176       runtime->GetHeap()->CollectGarbage(/* clear_soft_references */ true);
177       skipped_gc_iterations = 0;
178     }
179 
180     ASSERT_EQ(passed_class_verification, expected_success) << " Failed for " << name;
181   }
182 
TestFuzzerHelper(const std::string & archive_filename,const std::unordered_set<std::string> & valid_dex_files,std::function<void (const uint8_t *,size_t,const std::string &,bool)> verify_file)183   void TestFuzzerHelper(
184       const std::string& archive_filename,
185       const std::unordered_set<std::string>& valid_dex_files,
186       std::function<void(const uint8_t*, size_t, const std::string&, bool)> verify_file) {
187     // Consistency checks.
188     const std::string folder = android::base::GetExecutableDirectory();
189     ASSERT_TRUE(std::filesystem::is_directory(folder)) << folder << " is not a folder";
190     ASSERT_FALSE(std::filesystem::is_empty(folder)) << " No files found for directory " << folder;
191     const std::string filename = folder + "/" + archive_filename;
192 
193     // Iterate using ZipArchiveHandle. We have to be careful about managing the pointers with
194     // CloseArchive, StartIteration, and EndIteration.
195     std::string error_msg;
196     ZipArchiveHandle handle;
197     ZipArchiveHandleScope scope(&handle);
198     int32_t error = OpenArchive(filename.c_str(), &handle);
199     ASSERT_TRUE(error == 0) << "Error: " << error;
200 
201     void* cookie;
202     error = StartIteration(handle, &cookie);
203     ASSERT_TRUE(error == 0) << "couldn't iterate " << filename << " : " << ErrorCodeString(error);
204 
205     ZipEntry64 entry;
206     std::string name;
207     std::vector<char> data;
208     while ((error = Next(cookie, &entry, &name)) >= 0) {
209       if (!name.ends_with(".dex")) {
210         // Skip non-DEX files.
211         LOG(WARNING) << "Found a non-dex file: " << name;
212         continue;
213       }
214       data.resize(entry.uncompressed_length);
215       error = ExtractToMemory(handle, &entry, reinterpret_cast<uint8_t*>(data.data()), data.size());
216       ASSERT_TRUE(error == 0) << "failed to extract entry: " << name << " from " << filename << ""
217                               << ErrorCodeString(error);
218 
219       const uint8_t* file_data = reinterpret_cast<const uint8_t*>(data.data());
220       // Special case for empty dex file. Set a fake data since the size is 0 anyway.
221       if (file_data == nullptr) {
222         ASSERT_EQ(data.size(), 0);
223         file_data = reinterpret_cast<const uint8_t*>(&name);
224       }
225 
226       const bool is_valid_dex_file = valid_dex_files.find(name) != valid_dex_files.end();
227       verify_file(file_data, data.size(), name, is_valid_dex_file);
228     }
229 
230     ASSERT_TRUE(error >= -1) << "failed iterating " << filename << " : " << ErrorCodeString(error);
231     EndIteration(cookie);
232   }
233 
234  private:
RegisterDexFileAndGetClassLoader(Runtime * runtime,StandardDexFile * dex_file)235   static jobject RegisterDexFileAndGetClassLoader(Runtime* runtime, StandardDexFile* dex_file)
236       REQUIRES_SHARED(Locks::mutator_lock_) {
237     Thread* self = Thread::Current();
238     ClassLinker* class_linker = runtime->GetClassLinker();
239     const std::vector<const DexFile*> dex_files = {dex_file};
240     jobject class_loader = class_linker->CreatePathClassLoader(self, dex_files);
241     ObjPtr<mirror::ClassLoader> cl = self->DecodeJObject(class_loader)->AsClassLoader();
242     class_linker->RegisterDexFile(*dex_file, cl);
243     return class_loader;
244   }
245 };
246 
247 // Tests that we can verify dex files without crashing.
TEST_F(FuzzerCorpusTest,VerifyCorpusDexFiles)248 TEST_F(FuzzerCorpusTest, VerifyCorpusDexFiles) {
249   // These dex files are expected to pass verification. The others are regressions tests.
250   const std::unordered_set<std::string> valid_dex_files = {"Main.dex", "hello_world.dex"};
251   const std::string archive_filename = "dex_verification_fuzzer_corpus.zip";
252 
253   TestFuzzerHelper(archive_filename, valid_dex_files, DexFileVerification);
254 }
255 
256 // Tests that we can verify classes from dex files without crashing.
TEST_F(FuzzerCorpusTest,VerifyCorpusClassDexFiles)257 TEST_F(FuzzerCorpusTest, VerifyCorpusClassDexFiles) {
258   // These dex files are expected to pass verification. The others are regressions tests.
259   const std::unordered_set<std::string> valid_dex_files = {"Main.dex", "hello_world.dex"};
260   const std::string archive_filename = "class_verification_fuzzer_corpus.zip";
261 
262   TestFuzzerHelper(archive_filename, valid_dex_files, ClassVerification);
263 }
264 
265 }  // namespace art
266