blob: 6b1bb130c9c6e3fcf336df7ef4fe330753ed4141 [file] [log] [blame]
/*
* Copyright (C) 2023 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.
*/
#include <cstdint>
#include <filesystem>
#include <unordered_set>
#include "android-base/file.h"
#include "dex/dex_file_verifier.h"
#include "dex/standard_dex_file.h"
#include "gtest/gtest.h"
#include "ziparchive/zip_archive.h"
namespace art {
class FuzzerCorpusTest : public testing::Test {
public:
static void VerifyDexFile(const uint8_t* data,
size_t size,
const std::string& name,
bool expected_success) {
// Do not verify the checksum as we only care about the DEX file contents,
// and know that the checksum would probably be erroneous (i.e. random).
constexpr bool kVerify = false;
// Special case for empty dex file. Set a fake data since the size is 0 anyway.
if (data == nullptr) {
ASSERT_EQ(size, 0);
data = reinterpret_cast<const uint8_t*>(&name);
}
auto container = std::make_shared<art::MemoryDexFileContainer>(data, size);
art::StandardDexFile dex_file(data,
/*location=*/name,
/*location_checksum=*/0,
/*oat_dex_file=*/nullptr,
container);
std::string error_msg;
bool success = art::dex::Verify(&dex_file, dex_file.GetLocation().c_str(), kVerify, &error_msg);
ASSERT_EQ(success, expected_success) << " Failed for " << name;
}
};
// Class that manages the ZipArchiveHandle liveness.
class ZipArchiveHandleScope {
public:
explicit ZipArchiveHandleScope(ZipArchiveHandle* handle) : handle_(handle) {}
~ZipArchiveHandleScope() { CloseArchive(*(handle_.release())); }
private:
std::unique_ptr<ZipArchiveHandle> handle_;
};
// Returns true if `str` ends with `suffix`.
inline bool EndsWith(std::string const& str, std::string const& suffix) {
if (suffix.size() > str.size()) {
return false;
}
return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
}
// Tests that we can verify dex files without crashing.
TEST_F(FuzzerCorpusTest, VerifyCorpusDexFiles) {
// These dex files are expected to pass verification. The others are regressions tests.
const std::unordered_set<std::string> valid_dex_files = {"Main.dex", "hello_world.dex"};
// Consistency checks.
const std::string folder = android::base::GetExecutableDirectory();
ASSERT_TRUE(std::filesystem::is_directory(folder)) << folder << " is not a folder";
ASSERT_FALSE(std::filesystem::is_empty(folder)) << " No files found for directory " << folder;
const std::string filename = folder + "/fuzzer_corpus.zip";
// Iterate using ZipArchiveHandle. We have to be careful about managing the pointers with
// CloseArchive, StartIteration, and EndIteration.
std::string error_msg;
ZipArchiveHandle handle;
ZipArchiveHandleScope scope(&handle);
int32_t error = OpenArchive(filename.c_str(), &handle);
ASSERT_TRUE(error == 0) << "Error: " << error;
void* cookie;
error = StartIteration(handle, &cookie);
ASSERT_TRUE(error == 0) << "couldn't iterate " << filename << " : " << ErrorCodeString(error);
ZipEntry64 entry;
std::string name;
std::vector<char> data;
while ((error = Next(cookie, &entry, &name)) >= 0) {
if (!EndsWith(name, ".dex")) {
// Skip non-DEX files.
LOG(WARNING) << "Found a non-dex file: " << name;
continue;
}
data.resize(entry.uncompressed_length);
error = ExtractToMemory(handle, &entry, reinterpret_cast<uint8_t*>(data.data()), data.size());
ASSERT_TRUE(error == 0) << "failed to extract entry: " << name << " from " << filename << ""
<< ErrorCodeString(error);
const bool expected_success = valid_dex_files.find(name) != valid_dex_files.end();
VerifyDexFile(
reinterpret_cast<const uint8_t*>(data.data()), data.size(), name.c_str(), expected_success);
}
ASSERT_TRUE(error >= -1) << "failed iterating " << filename << " : " << ErrorCodeString(error);
EndIteration(cookie);
}
} // namespace art