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 "dex/dex_file_verifier.h"
23 #include "dex/standard_dex_file.h"
24 #include "gtest/gtest.h"
25 #include "ziparchive/zip_archive.h"
26 
27 namespace art {
28 
29 class FuzzerCorpusTest : public testing::Test {
30  public:
VerifyDexFile(const uint8_t * data,size_t size,const std::string & name,bool expected_success)31   static void VerifyDexFile(const uint8_t* data,
32                             size_t size,
33                             const std::string& name,
34                             bool expected_success) {
35     // Do not verify the checksum as we only care about the DEX file contents,
36     // and know that the checksum would probably be erroneous (i.e. random).
37     constexpr bool kVerify = false;
38 
39     // Special case for empty dex file. Set a fake data since the size is 0 anyway.
40     if (data == nullptr) {
41       ASSERT_EQ(size, 0);
42       data = reinterpret_cast<const uint8_t*>(&name);
43     }
44 
45     auto container = std::make_shared<art::MemoryDexFileContainer>(data, size);
46     art::StandardDexFile dex_file(data,
47                                   /*location=*/name,
48                                   /*location_checksum=*/0,
49                                   /*oat_dex_file=*/nullptr,
50                                   container);
51 
52     std::string error_msg;
53     bool success = art::dex::Verify(&dex_file, dex_file.GetLocation().c_str(), kVerify, &error_msg);
54     ASSERT_EQ(success, expected_success) << " Failed for " << name;
55   }
56 };
57 
58 // Class that manages the ZipArchiveHandle liveness.
59 class ZipArchiveHandleScope {
60  public:
ZipArchiveHandleScope(ZipArchiveHandle * handle)61   explicit ZipArchiveHandleScope(ZipArchiveHandle* handle) : handle_(handle) {}
~ZipArchiveHandleScope()62   ~ZipArchiveHandleScope() { CloseArchive(*(handle_.release())); }
63 
64  private:
65   std::unique_ptr<ZipArchiveHandle> handle_;
66 };
67 
68 // Tests that we can verify dex files without crashing.
TEST_F(FuzzerCorpusTest,VerifyCorpusDexFiles)69 TEST_F(FuzzerCorpusTest, VerifyCorpusDexFiles) {
70   // These dex files are expected to pass verification. The others are regressions tests.
71   const std::unordered_set<std::string> valid_dex_files = {"Main.dex", "hello_world.dex"};
72 
73   // Consistency checks.
74   const std::string folder = android::base::GetExecutableDirectory();
75   ASSERT_TRUE(std::filesystem::is_directory(folder)) << folder << " is not a folder";
76   ASSERT_FALSE(std::filesystem::is_empty(folder)) << " No files found for directory " << folder;
77 
78   const std::string filename = folder + "/fuzzer_corpus.zip";
79 
80   // Iterate using ZipArchiveHandle. We have to be careful about managing the pointers with
81   // CloseArchive, StartIteration, and EndIteration.
82   std::string error_msg;
83   ZipArchiveHandle handle;
84   ZipArchiveHandleScope scope(&handle);
85   int32_t error = OpenArchive(filename.c_str(), &handle);
86   ASSERT_TRUE(error == 0) << "Error: " << error;
87 
88   void* cookie;
89   error = StartIteration(handle, &cookie);
90   ASSERT_TRUE(error == 0) << "couldn't iterate " << filename << " : " << ErrorCodeString(error);
91 
92   ZipEntry64 entry;
93   std::string name;
94   std::vector<char> data;
95   while ((error = Next(cookie, &entry, &name)) >= 0) {
96     if (!name.ends_with(".dex")) {
97       // Skip non-DEX files.
98       LOG(WARNING) << "Found a non-dex file: " << name;
99       continue;
100     }
101     data.resize(entry.uncompressed_length);
102     error = ExtractToMemory(handle, &entry, reinterpret_cast<uint8_t*>(data.data()), data.size());
103     ASSERT_TRUE(error == 0) << "failed to extract entry: " << name << " from " << filename << ""
104                             << ErrorCodeString(error);
105 
106     const bool expected_success = valid_dex_files.find(name) != valid_dex_files.end();
107     VerifyDexFile(
108         reinterpret_cast<const uint8_t*>(data.data()), data.size(), name, expected_success);
109   }
110 
111   ASSERT_TRUE(error >= -1) << "failed iterating " << filename << " : " << ErrorCodeString(error);
112   EndIteration(cookie);
113 }
114 
115 }  // namespace art
116