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