| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/elfldltl/compat-hash.h> |
| #include <lib/elfldltl/fuzzer.h> |
| #include <lib/elfldltl/gnu-hash.h> |
| #include <lib/elfldltl/layout.h> |
| #include <lib/elfldltl/symbol.h> |
| #include <zircon/assert.h> |
| |
| namespace { |
| |
| template <typename Hasher> |
| void HashFuzzer(Hasher&& hasher, uint32_t no, FuzzedDataProvider& provider) { |
| auto name = provider.ConsumeRandomLengthString(); |
| uint32_t hash = std::forward<Hasher>(hasher)(name); |
| ZX_ASSERT(hash != no); |
| } |
| |
| template <class Elf> |
| struct SymbolFuzzer { |
| using SymbolInfo = elfldltl::SymbolInfo<Elf>; |
| |
| using FuzzerInputs = elfldltl::FuzzerInput< |
| // There are five separate inputs. Only DT_SYMTAB and DT_GNU_HASH really |
| // need the Addr alignment. DT_STRTAB needs no alignment at all and |
| // DT_HASH needs only Word alignment. |
| sizeof(typename SymbolInfo::Addr), |
| typename SymbolInfo::Sym, // 1. DT_SYMTAB |
| typename SymbolInfo::Addr, // 2. DT_GNU_HASH |
| typename SymbolInfo::Word, // 3. DT_HASH |
| char, // 4. DT_STRTAB |
| uint8_t>; // 5. Provider for further fuzzing. |
| |
| // This just exhaustively traverses the hash table and calls fuzz(symndx). |
| template <class HashTable, typename T> |
| static void HashBucketFuzzer(std::optional<HashTable> table, T&& fuzz) { |
| using HashBucket = typename SymbolInfo::template HashBucket<HashTable>; |
| if (table) { |
| for (uint32_t i = 0; i < table->size(); ++i) { |
| for (uint32_t symndx : HashBucket(*table, i, i)) { |
| fuzz(symndx); |
| } |
| } |
| } |
| } |
| |
| int operator()(FuzzedDataProvider& provider) const { |
| FuzzerInputs inputs(provider); |
| auto [symtab, gnu_hash, compat_hash, strtab, blob] = inputs.inputs(); |
| |
| // Use the inputs to populate a SymbolInfo. |
| SymbolInfo info; |
| info.set_symtab(symtab) |
| .set_strtab({strtab.data(), strtab.size()}) |
| .set_compat_hash(compat_hash) |
| .set_gnu_hash(gnu_hash); |
| |
| auto fuzz_hash_table_entry = // Do some exhaustive iteration tests. |
| [safe_symtab = info.safe_symtab(), &info](uint32_t symndx) { |
| if (symndx < safe_symtab.size()) { |
| std::string_view name = info.string(safe_symtab[symndx].name); |
| for (char c : name) { |
| // This doesn't do anything but ensures the compiler has to |
| // generate a dereference of each character in case the pointer |
| // or size is bad. |
| __asm__ volatile("" : : "r"(c)); |
| } |
| } |
| }; |
| HashBucketFuzzer(info.compat_hash(), fuzz_hash_table_entry); |
| HashBucketFuzzer(info.gnu_hash(), fuzz_hash_table_entry); |
| |
| // The last input drives the rest of the operation of the fuzzer. |
| // Do random lookups until the provider is out of data. |
| FuzzedDataProvider blob_provider(blob.data(), blob.size()); |
| while (blob_provider.remaining_bytes() > 0) { |
| const std::string name = blob_provider.ConsumeRandomLengthString(); |
| if (const auto* sym = elfldltl::SymbolName(name).Lookup(info)) { |
| ZX_ASSERT(info.string(sym->name) == name); |
| } |
| } |
| |
| return 0; |
| } |
| }; |
| |
| using Fuzzer = elfldltl::ElfFuzzer<SymbolFuzzer>; |
| |
| } // namespace |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| FuzzedDataProvider provider(data, size); |
| HashFuzzer(elfldltl::CompatHashString, elfldltl::kCompatNoHash, provider); |
| HashFuzzer(elfldltl::GnuHashString, elfldltl::kGnuNoHash, provider); |
| return Fuzzer{}(provider); |
| } |