Loading libunwindstack/Android.bp +32 −0 Original line number Diff line number Diff line Loading @@ -340,6 +340,37 @@ cc_test { isolated: true, } //------------------------------------------------------------------------- // Fuzzers //------------------------------------------------------------------------- cc_defaults { name: "libunwindstack_fuzz_defaults", host_supported: true, defaults: ["libunwindstack_flags"], cflags: [ "-Wno-exit-time-destructors", "-g", ], shared_libs: [ "libbase", "liblog", "liblzma", "libunwindstack", "libdexfile_support", ], } cc_fuzz { name: "libunwindstack_fuzz_unwinder", defaults: ["libunwindstack_fuzz_defaults"], srcs: [ "tests/MemoryFake.cpp", "tests/ElfFake.cpp", "tests/fuzz/UnwinderComponentCreator.cpp", "tests/fuzz/UnwinderFuzz.cpp", ], } //------------------------------------------------------------------------- // Tools //------------------------------------------------------------------------- Loading Loading @@ -458,3 +489,4 @@ cc_binary_host { "tests/GenGnuDebugdata.cpp", ], } libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp 0 → 100644 +356 −0 Original line number Diff line number Diff line /* * Copyright 2020 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 "UnwinderComponentCreator.h" std::unique_ptr<Regs> GetRegisters(ArchEnum arch) { switch (arch) { case unwindstack::ARCH_ARM: { std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>(); return regs; } case unwindstack::ARCH_ARM64: { std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>(); return regs; } case unwindstack::ARCH_X86: { std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>(); return regs; } case unwindstack::ARCH_X86_64: { std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>(); return regs; } case unwindstack::ARCH_MIPS: { std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>(); return regs; } case unwindstack::ARCH_MIPS64: { std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>(); return regs; } case unwindstack::ARCH_UNKNOWN: default: { std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>(); return regs; } } } ArchEnum GetArch(FuzzedDataProvider* data_provider) { uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount); return static_cast<ArchEnum>(arch); } void ElfAddMapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name, Elf* elf = nullptr) { std::string str_name(name); maps->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1)); if (elf != nullptr) { const auto& map_info = *--maps->end(); map_info->elf.reset(elf); } } void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) { uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount); for (uint8_t i = 0; i < func_count; i++) { std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen); bool global = data_provider->ConsumeBool(); if (global) { elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>()); } else { ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i)); } } } void ElfPushFakeStepData(FuzzedDataProvider* data_provider) { uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount); for (uint8_t i = 0; i < step_count; i++) { uint64_t pc = data_provider->ConsumeIntegral<uint64_t>(); uint64_t sp = data_provider->ConsumeIntegral<uint64_t>(); bool finished = i + 1 == step_count; ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished)); } } ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) { // This will be passed to a smart pointer in ElfAddMapInfo. ElfFake* elf = new ElfFake(new MemoryFake); // This will be handled by a smart pointer within Elf. ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr); std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen); interface_fake->FakeSetBuildID(build_id); std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen); interface_fake->FakeSetSoname(so_name.c_str()); elf->FakeSetArch(GetArch(data_provider)); elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>()); ElfPushFakeFunctionData(data_provider, interface_fake); ElfPushFakeStepData(data_provider); elf->FakeSetInterface(interface_fake); ElfInterfaceFake::FakeClear(); return elf; } std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) { std::unique_ptr<Maps> maps = std::make_unique<Maps>(); uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount); for (uint8_t i = 0; i < entry_count; i++) { uint64_t start = data_provider->ConsumeIntegral<uint64_t>(); uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX); uint64_t offset = data_provider->ConsumeIntegral<uint64_t>(); std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen); uint8_t flags = PROT_READ | PROT_WRITE; bool exec = data_provider->ConsumeBool(); if (exec) { flags |= PROT_EXEC; } bool shouldAddElf = data_provider->ConsumeBool(); if (shouldAddElf) { ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(), PopulateElfFake(data_provider)); } else { ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str()); } } maps->Sort(); return maps; } // This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp // There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and // PutElfInMemory inserts JIT data when called. void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) { // Format of the 32 bit JITDescriptor structure: // uint32_t version memory->SetData32(addr, 1); // uint32_t action_flag memory->SetData32(addr + 4, 0); // uint32_t relevant_entry memory->SetData32(addr + 8, 0); // uint32_t first_entry memory->SetData32(addr + 12, entry); } void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) { // Format of the 64 bit JITDescriptor structure: // uint32_t version memory->SetData32(addr, 1); // uint32_t action_flag memory->SetData32(addr + 4, 0); // uint64_t relevant_entry memory->SetData64(addr + 8, 0); // uint64_t first_entry memory->SetData64(addr + 16, entry); } void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, uint64_t elf_size) { // Format of the 32 bit JITCodeEntry structure: // uint32_t next memory->SetData32(addr, next); // uint32_t prev memory->SetData32(addr + 4, prev); // uint32_t symfile_addr memory->SetData32(addr + 8, elf_addr); // uint64_t symfile_size memory->SetData64(addr + 12, elf_size); } void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, uint64_t elf_size) { // Format of the 32 bit JITCodeEntry structure: // uint32_t next memory->SetData32(addr, next); // uint32_t prev memory->SetData32(addr + 4, prev); // uint32_t symfile_addr memory->SetData32(addr + 8, elf_addr); // uint32_t pad memory->SetData32(addr + 12, 0); // uint64_t symfile_size memory->SetData64(addr + 16, elf_size); } void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr, uint64_t elf_size) { // Format of the 64 bit JITCodeEntry structure: // uint64_t next memory->SetData64(addr, next); // uint64_t prev memory->SetData64(addr + 8, prev); // uint64_t symfile_addr memory->SetData64(addr + 16, elf_addr); // uint64_t symfile_size memory->SetData64(addr + 24, elf_size); } template <typename EhdrType, typename ShdrType> void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc, uint32_t size) { EhdrType ehdr; memset(&ehdr, 0, sizeof(ehdr)); uint64_t sh_offset = sizeof(ehdr); memcpy(ehdr.e_ident, ELFMAG, SELFMAG); ehdr.e_ident[EI_CLASS] = class_type; ehdr.e_machine = machine_type; ehdr.e_shstrndx = 1; ehdr.e_shoff = sh_offset; ehdr.e_shentsize = sizeof(ShdrType); ehdr.e_shnum = 3; memory->SetMemory(offset, &ehdr, sizeof(ehdr)); ShdrType shdr; memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_NULL; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); sh_offset += sizeof(shdr); memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_STRTAB; shdr.sh_name = 1; shdr.sh_offset = 0x500; shdr.sh_size = 0x100; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); memory->SetMemory(offset + 0x500, ".debug_frame"); sh_offset += sizeof(shdr); memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_PROGBITS; shdr.sh_name = 0; shdr.sh_addr = 0x600; shdr.sh_offset = 0x600; shdr.sh_size = 0x200; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); // Now add a single cie/fde. uint64_t dwarf_offset = offset + 0x600; if (class_type == ELFCLASS32) { // CIE 32 information. memory->SetData32(dwarf_offset, 0xfc); memory->SetData32(dwarf_offset + 0x4, 0xffffffff); memory->SetData8(dwarf_offset + 0x8, 1); memory->SetData8(dwarf_offset + 0x9, '\0'); memory->SetData8(dwarf_offset + 0xa, 0x4); memory->SetData8(dwarf_offset + 0xb, 0x4); memory->SetData8(dwarf_offset + 0xc, 0x1); // FDE 32 information. memory->SetData32(dwarf_offset + 0x100, 0xfc); memory->SetData32(dwarf_offset + 0x104, 0); memory->SetData32(dwarf_offset + 0x108, pc); memory->SetData32(dwarf_offset + 0x10c, size); } else { // CIE 64 information. memory->SetData32(dwarf_offset, 0xffffffff); memory->SetData64(dwarf_offset + 4, 0xf4); memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL); memory->SetData8(dwarf_offset + 0x14, 1); memory->SetData8(dwarf_offset + 0x15, '\0'); memory->SetData8(dwarf_offset + 0x16, 0x4); memory->SetData8(dwarf_offset + 0x17, 0x4); memory->SetData8(dwarf_offset + 0x18, 0x1); // FDE 64 information. memory->SetData32(dwarf_offset + 0x100, 0xffffffff); memory->SetData64(dwarf_offset + 0x104, 0xf4); memory->SetData64(dwarf_offset + 0x10c, 0); memory->SetData64(dwarf_offset + 0x114, pc); memory->SetData64(dwarf_offset + 0x11c, size); } } void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) { uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles); int entry_offset = 0; int prev_jit_addr = 0; for (uint8_t i = 0; i < elf_file_count; i++) { uint64_t offset = data_provider->ConsumeIntegral<uint64_t>(); // Technically the max valid value is ELFCLASSNUM - 1 (2), but // we want to test values outside of that range. uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>(); // Same here, EM_NUM is 253, max valid machine type is 252 uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>(); uint32_t pc = data_provider->ConsumeIntegral<uint32_t>(); uint32_t size = data_provider->ConsumeIntegral<uint32_t>(); bool sixty_four_bit = data_provider->ConsumeBool(); bool write_jit = data_provider->ConsumeBool(); if (sixty_four_bit) { PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size); } else { PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size); } if (write_jit) { bool use_pad = data_provider->ConsumeBool(); // It is possible this will overwrite part of the ELF. // This provides an interesting test of how malformed ELF // data is handled. uint64_t cur_descriptor_addr = 0x11800 + entry_offset; uint64_t cur_jit_addr = 0x200000 + entry_offset; uint64_t next_jit_addr = cur_jit_addr + size; if (sixty_four_bit) { WriteDescriptor64(memory, 0x11800, cur_jit_addr); WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } else { // Loop back. Again, this may corrupt data, // but that will allow for testing edge cases with // malformed JIT data. if (cur_jit_addr > UINT32_MAX) { entry_offset = 0; cur_jit_addr = 0x200000; cur_descriptor_addr = 0x11800; next_jit_addr = cur_jit_addr + size; } WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr); if (use_pad) { WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } else { WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } } entry_offset += size; prev_jit_addr = cur_jit_addr; } } } std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len, uint max_strings) { uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings); std::vector<std::string> strings; for (uint i = 0; i < str_count; i++) { strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len)); } return strings; } std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider, std::shared_ptr<Memory> memory, uint max_library_length, uint max_libraries) { std::vector<std::string> search_libs = GetStringList(data_provider, max_library_length, max_libraries); if (search_libs.size() <= 0) { return std::make_unique<DexFiles>(memory); } return std::make_unique<DexFiles>(memory, search_libs); } libunwindstack/tests/fuzz/UnwinderComponentCreator.h 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright 2020 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. */ #ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H #define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H #include <elf.h> #include <sys/mman.h> #include <memory> #include <string> #include <vector> #include <fuzzer/FuzzedDataProvider.h> #include <unwindstack/DexFiles.h> #include <unwindstack/Maps.h> #include <unwindstack/Regs.h> #include <unwindstack/RegsArm.h> #include <unwindstack/RegsArm64.h> #include <unwindstack/RegsMips.h> #include <unwindstack/RegsMips64.h> #include <unwindstack/RegsX86.h> #include <unwindstack/RegsX86_64.h> #include "../ElfFake.h" #include "../MemoryFake.h" #include "fuzzer/FuzzedDataProvider.h" using unwindstack::ArchEnum; using unwindstack::DexFiles; using unwindstack::Elf; using unwindstack::ElfFake; using unwindstack::ElfInterfaceFake; using unwindstack::FunctionData; using unwindstack::Maps; using unwindstack::Memory; using unwindstack::MemoryFake; using unwindstack::Regs; using unwindstack::StepData; static constexpr uint8_t kArchCount = 6; static constexpr uint8_t kMaxSoNameLen = 150; static constexpr uint8_t kMaxFuncNameLen = 50; static constexpr uint8_t kMaxFuncCount = 100; static constexpr uint8_t kMaxJitElfFiles = 20; static constexpr uint8_t kJitElfPadding = 32; static constexpr uint8_t kMaxStepCount = 100; static constexpr uint8_t kMaxMapEntryCount = 50; static constexpr uint8_t kMaxBuildIdLen = 100; static constexpr uint8_t kMaxMapInfoNameLen = 150; std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch); std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider); std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len, uint max_strings); unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider); void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name, Elf* elf = nullptr); void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider); std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider, std::shared_ptr<unwindstack::Memory> memory, uint max_libraries, uint max_library_length); #endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H libunwindstack/tests/fuzz/UnwinderFuzz.cpp 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright 2020 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 <functional> #include <iostream> #include <vector> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> #include <unwindstack/Unwinder.h> #include "../MemoryFake.h" #include "UnwinderComponentCreator.h" #include "fuzzer/FuzzedDataProvider.h" namespace unwindstack { static constexpr int kMaxUnwindStringLen = 50; static constexpr int kMaxUnwindStrings = 50; void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) { // 0 = don't set any values // 1 = set initial_map_names_to_skip // 2 = set map_suffixes_to_ignore // 3 = set both uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4; if (set_values == 0) { unwinder->Unwind(); } else if (set_values == 1) { // Only setting initial_map_names_to_skip std::vector<std::string> skip_names = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(&skip_names, nullptr); } else if (set_values == 2) { // Only setting map_suffixes_to_ignore std::vector<std::string> ignore_suffixes = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(nullptr, &ignore_suffixes); } else if (set_values == 3) { // Setting both values std::vector<std::string> skip_names = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); std::vector<std::string> ignore_suffixes = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(&skip_names, &ignore_suffixes); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider data_provider(data, size); // We need to construct an unwinder. // Generate the Maps: std::unique_ptr<Maps> maps = GetMaps(&data_provider); // Generate the Regs: uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount); ArchEnum arch = static_cast<ArchEnum>(arch_val); std::unique_ptr<Regs> regs = GetRegisters(arch); // Generate memory: std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>(); PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider); size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000); std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory); // Create instance Unwinder unwinder(max_frames, maps.get(), regs.get(), memory); unwinder.SetJitDebug(jit_debug_ptr.get(), arch); unwinder.SetResolveNames(data_provider.ConsumeBool()); // Call unwind PerformUnwind(&data_provider, &unwinder); // Run some additional logic that changes after unwind uint64_t pc = data_provider.ConsumeIntegral<uint64_t>(); unwinder.BuildFrameFromPcOnly(pc); unwinder.ConsumeFrames(); return 0; } } // namespace unwindstack Loading
libunwindstack/Android.bp +32 −0 Original line number Diff line number Diff line Loading @@ -340,6 +340,37 @@ cc_test { isolated: true, } //------------------------------------------------------------------------- // Fuzzers //------------------------------------------------------------------------- cc_defaults { name: "libunwindstack_fuzz_defaults", host_supported: true, defaults: ["libunwindstack_flags"], cflags: [ "-Wno-exit-time-destructors", "-g", ], shared_libs: [ "libbase", "liblog", "liblzma", "libunwindstack", "libdexfile_support", ], } cc_fuzz { name: "libunwindstack_fuzz_unwinder", defaults: ["libunwindstack_fuzz_defaults"], srcs: [ "tests/MemoryFake.cpp", "tests/ElfFake.cpp", "tests/fuzz/UnwinderComponentCreator.cpp", "tests/fuzz/UnwinderFuzz.cpp", ], } //------------------------------------------------------------------------- // Tools //------------------------------------------------------------------------- Loading Loading @@ -458,3 +489,4 @@ cc_binary_host { "tests/GenGnuDebugdata.cpp", ], }
libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp 0 → 100644 +356 −0 Original line number Diff line number Diff line /* * Copyright 2020 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 "UnwinderComponentCreator.h" std::unique_ptr<Regs> GetRegisters(ArchEnum arch) { switch (arch) { case unwindstack::ARCH_ARM: { std::unique_ptr<unwindstack::RegsArm> regs = std::make_unique<unwindstack::RegsArm>(); return regs; } case unwindstack::ARCH_ARM64: { std::unique_ptr<unwindstack::RegsArm64> regs = std::make_unique<unwindstack::RegsArm64>(); return regs; } case unwindstack::ARCH_X86: { std::unique_ptr<unwindstack::RegsX86> regs = std::make_unique<unwindstack::RegsX86>(); return regs; } case unwindstack::ARCH_X86_64: { std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>(); return regs; } case unwindstack::ARCH_MIPS: { std::unique_ptr<unwindstack::RegsMips> regs = std::make_unique<unwindstack::RegsMips>(); return regs; } case unwindstack::ARCH_MIPS64: { std::unique_ptr<unwindstack::RegsMips64> regs = std::make_unique<unwindstack::RegsMips64>(); return regs; } case unwindstack::ARCH_UNKNOWN: default: { std::unique_ptr<unwindstack::RegsX86_64> regs = std::make_unique<unwindstack::RegsX86_64>(); return regs; } } } ArchEnum GetArch(FuzzedDataProvider* data_provider) { uint8_t arch = data_provider->ConsumeIntegralInRange<uint8_t>(1, kArchCount); return static_cast<ArchEnum>(arch); } void ElfAddMapInfo(Maps* maps, uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name, Elf* elf = nullptr) { std::string str_name(name); maps->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1)); if (elf != nullptr) { const auto& map_info = *--maps->end(); map_info->elf.reset(elf); } } void ElfPushFakeFunctionData(FuzzedDataProvider* data_provider, ElfInterfaceFake* elf) { uint8_t func_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxFuncCount); for (uint8_t i = 0; i < func_count; i++) { std::string func_name = data_provider->ConsumeRandomLengthString(kMaxFuncNameLen); bool global = data_provider->ConsumeBool(); if (global) { elf->FakeSetGlobalVariable(func_name, data_provider->ConsumeIntegral<uint64_t>()); } else { ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i)); } } } void ElfPushFakeStepData(FuzzedDataProvider* data_provider) { uint8_t step_count = data_provider->ConsumeIntegralInRange<uint>(0, kMaxStepCount); for (uint8_t i = 0; i < step_count; i++) { uint64_t pc = data_provider->ConsumeIntegral<uint64_t>(); uint64_t sp = data_provider->ConsumeIntegral<uint64_t>(); bool finished = i + 1 == step_count; ElfInterfaceFake::FakePushStepData(StepData(pc, sp, finished)); } } ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) { // This will be passed to a smart pointer in ElfAddMapInfo. ElfFake* elf = new ElfFake(new MemoryFake); // This will be handled by a smart pointer within Elf. ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr); std::string build_id = data_provider->ConsumeRandomLengthString(kMaxBuildIdLen); interface_fake->FakeSetBuildID(build_id); std::string so_name = data_provider->ConsumeRandomLengthString(kMaxSoNameLen); interface_fake->FakeSetSoname(so_name.c_str()); elf->FakeSetArch(GetArch(data_provider)); elf->FakeSetLoadBias(data_provider->ConsumeIntegral<uint64_t>()); ElfPushFakeFunctionData(data_provider, interface_fake); ElfPushFakeStepData(data_provider); elf->FakeSetInterface(interface_fake); ElfInterfaceFake::FakeClear(); return elf; } std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) { std::unique_ptr<Maps> maps = std::make_unique<Maps>(); uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount); for (uint8_t i = 0; i < entry_count; i++) { uint64_t start = data_provider->ConsumeIntegral<uint64_t>(); uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX); uint64_t offset = data_provider->ConsumeIntegral<uint64_t>(); std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen); uint8_t flags = PROT_READ | PROT_WRITE; bool exec = data_provider->ConsumeBool(); if (exec) { flags |= PROT_EXEC; } bool shouldAddElf = data_provider->ConsumeBool(); if (shouldAddElf) { ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str(), PopulateElfFake(data_provider)); } else { ElfAddMapInfo(maps.get(), start, end, offset, flags, map_info_name.c_str()); } } maps->Sort(); return maps; } // This code (until PutElfFilesInMemory) is pretty much directly copied from JitDebugTest.cpp // There's a few minor modifications, most notably, all methods accept a MemoryFake pointer, and // PutElfInMemory inserts JIT data when called. void WriteDescriptor32(MemoryFake* memory, uint64_t addr, uint32_t entry) { // Format of the 32 bit JITDescriptor structure: // uint32_t version memory->SetData32(addr, 1); // uint32_t action_flag memory->SetData32(addr + 4, 0); // uint32_t relevant_entry memory->SetData32(addr + 8, 0); // uint32_t first_entry memory->SetData32(addr + 12, entry); } void WriteDescriptor64(MemoryFake* memory, uint64_t addr, uint64_t entry) { // Format of the 64 bit JITDescriptor structure: // uint32_t version memory->SetData32(addr, 1); // uint32_t action_flag memory->SetData32(addr + 4, 0); // uint64_t relevant_entry memory->SetData64(addr + 8, 0); // uint64_t first_entry memory->SetData64(addr + 16, entry); } void WriteEntry32Pack(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, uint64_t elf_size) { // Format of the 32 bit JITCodeEntry structure: // uint32_t next memory->SetData32(addr, next); // uint32_t prev memory->SetData32(addr + 4, prev); // uint32_t symfile_addr memory->SetData32(addr + 8, elf_addr); // uint64_t symfile_size memory->SetData64(addr + 12, elf_size); } void WriteEntry32Pad(MemoryFake* memory, uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr, uint64_t elf_size) { // Format of the 32 bit JITCodeEntry structure: // uint32_t next memory->SetData32(addr, next); // uint32_t prev memory->SetData32(addr + 4, prev); // uint32_t symfile_addr memory->SetData32(addr + 8, elf_addr); // uint32_t pad memory->SetData32(addr + 12, 0); // uint64_t symfile_size memory->SetData64(addr + 16, elf_size); } void WriteEntry64(MemoryFake* memory, uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr, uint64_t elf_size) { // Format of the 64 bit JITCodeEntry structure: // uint64_t next memory->SetData64(addr, next); // uint64_t prev memory->SetData64(addr + 8, prev); // uint64_t symfile_addr memory->SetData64(addr + 16, elf_addr); // uint64_t symfile_size memory->SetData64(addr + 24, elf_size); } template <typename EhdrType, typename ShdrType> void PutElfInMemory(MemoryFake* memory, uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc, uint32_t size) { EhdrType ehdr; memset(&ehdr, 0, sizeof(ehdr)); uint64_t sh_offset = sizeof(ehdr); memcpy(ehdr.e_ident, ELFMAG, SELFMAG); ehdr.e_ident[EI_CLASS] = class_type; ehdr.e_machine = machine_type; ehdr.e_shstrndx = 1; ehdr.e_shoff = sh_offset; ehdr.e_shentsize = sizeof(ShdrType); ehdr.e_shnum = 3; memory->SetMemory(offset, &ehdr, sizeof(ehdr)); ShdrType shdr; memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_NULL; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); sh_offset += sizeof(shdr); memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_STRTAB; shdr.sh_name = 1; shdr.sh_offset = 0x500; shdr.sh_size = 0x100; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); memory->SetMemory(offset + 0x500, ".debug_frame"); sh_offset += sizeof(shdr); memset(&shdr, 0, sizeof(shdr)); shdr.sh_type = SHT_PROGBITS; shdr.sh_name = 0; shdr.sh_addr = 0x600; shdr.sh_offset = 0x600; shdr.sh_size = 0x200; memory->SetMemory(offset + sh_offset, &shdr, sizeof(shdr)); // Now add a single cie/fde. uint64_t dwarf_offset = offset + 0x600; if (class_type == ELFCLASS32) { // CIE 32 information. memory->SetData32(dwarf_offset, 0xfc); memory->SetData32(dwarf_offset + 0x4, 0xffffffff); memory->SetData8(dwarf_offset + 0x8, 1); memory->SetData8(dwarf_offset + 0x9, '\0'); memory->SetData8(dwarf_offset + 0xa, 0x4); memory->SetData8(dwarf_offset + 0xb, 0x4); memory->SetData8(dwarf_offset + 0xc, 0x1); // FDE 32 information. memory->SetData32(dwarf_offset + 0x100, 0xfc); memory->SetData32(dwarf_offset + 0x104, 0); memory->SetData32(dwarf_offset + 0x108, pc); memory->SetData32(dwarf_offset + 0x10c, size); } else { // CIE 64 information. memory->SetData32(dwarf_offset, 0xffffffff); memory->SetData64(dwarf_offset + 4, 0xf4); memory->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL); memory->SetData8(dwarf_offset + 0x14, 1); memory->SetData8(dwarf_offset + 0x15, '\0'); memory->SetData8(dwarf_offset + 0x16, 0x4); memory->SetData8(dwarf_offset + 0x17, 0x4); memory->SetData8(dwarf_offset + 0x18, 0x1); // FDE 64 information. memory->SetData32(dwarf_offset + 0x100, 0xffffffff); memory->SetData64(dwarf_offset + 0x104, 0xf4); memory->SetData64(dwarf_offset + 0x10c, 0); memory->SetData64(dwarf_offset + 0x114, pc); memory->SetData64(dwarf_offset + 0x11c, size); } } void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider) { uint8_t elf_file_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxJitElfFiles); int entry_offset = 0; int prev_jit_addr = 0; for (uint8_t i = 0; i < elf_file_count; i++) { uint64_t offset = data_provider->ConsumeIntegral<uint64_t>(); // Technically the max valid value is ELFCLASSNUM - 1 (2), but // we want to test values outside of that range. uint8_t class_type = data_provider->ConsumeIntegral<uint8_t>(); // Same here, EM_NUM is 253, max valid machine type is 252 uint8_t machine_type = data_provider->ConsumeIntegral<uint8_t>(); uint32_t pc = data_provider->ConsumeIntegral<uint32_t>(); uint32_t size = data_provider->ConsumeIntegral<uint32_t>(); bool sixty_four_bit = data_provider->ConsumeBool(); bool write_jit = data_provider->ConsumeBool(); if (sixty_four_bit) { PutElfInMemory<Elf64_Ehdr, Elf64_Shdr>(memory, offset, class_type, machine_type, pc, size); } else { PutElfInMemory<Elf32_Ehdr, Elf32_Shdr>(memory, offset, class_type, machine_type, pc, size); } if (write_jit) { bool use_pad = data_provider->ConsumeBool(); // It is possible this will overwrite part of the ELF. // This provides an interesting test of how malformed ELF // data is handled. uint64_t cur_descriptor_addr = 0x11800 + entry_offset; uint64_t cur_jit_addr = 0x200000 + entry_offset; uint64_t next_jit_addr = cur_jit_addr + size; if (sixty_four_bit) { WriteDescriptor64(memory, 0x11800, cur_jit_addr); WriteEntry64(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } else { // Loop back. Again, this may corrupt data, // but that will allow for testing edge cases with // malformed JIT data. if (cur_jit_addr > UINT32_MAX) { entry_offset = 0; cur_jit_addr = 0x200000; cur_descriptor_addr = 0x11800; next_jit_addr = cur_jit_addr + size; } WriteDescriptor32(memory, cur_descriptor_addr, cur_jit_addr); if (use_pad) { WriteEntry32Pad(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } else { WriteEntry32Pack(memory, cur_jit_addr, prev_jit_addr, next_jit_addr, pc, size); } } entry_offset += size; prev_jit_addr = cur_jit_addr; } } } std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len, uint max_strings) { uint str_count = data_provider->ConsumeIntegralInRange<uint>(0, max_strings); std::vector<std::string> strings; for (uint i = 0; i < str_count; i++) { strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len)); } return strings; } std::unique_ptr<DexFiles> GetDexFiles(FuzzedDataProvider* data_provider, std::shared_ptr<Memory> memory, uint max_library_length, uint max_libraries) { std::vector<std::string> search_libs = GetStringList(data_provider, max_library_length, max_libraries); if (search_libs.size() <= 0) { return std::make_unique<DexFiles>(memory); } return std::make_unique<DexFiles>(memory, search_libs); }
libunwindstack/tests/fuzz/UnwinderComponentCreator.h 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright 2020 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. */ #ifndef _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H #define _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H #include <elf.h> #include <sys/mman.h> #include <memory> #include <string> #include <vector> #include <fuzzer/FuzzedDataProvider.h> #include <unwindstack/DexFiles.h> #include <unwindstack/Maps.h> #include <unwindstack/Regs.h> #include <unwindstack/RegsArm.h> #include <unwindstack/RegsArm64.h> #include <unwindstack/RegsMips.h> #include <unwindstack/RegsMips64.h> #include <unwindstack/RegsX86.h> #include <unwindstack/RegsX86_64.h> #include "../ElfFake.h" #include "../MemoryFake.h" #include "fuzzer/FuzzedDataProvider.h" using unwindstack::ArchEnum; using unwindstack::DexFiles; using unwindstack::Elf; using unwindstack::ElfFake; using unwindstack::ElfInterfaceFake; using unwindstack::FunctionData; using unwindstack::Maps; using unwindstack::Memory; using unwindstack::MemoryFake; using unwindstack::Regs; using unwindstack::StepData; static constexpr uint8_t kArchCount = 6; static constexpr uint8_t kMaxSoNameLen = 150; static constexpr uint8_t kMaxFuncNameLen = 50; static constexpr uint8_t kMaxFuncCount = 100; static constexpr uint8_t kMaxJitElfFiles = 20; static constexpr uint8_t kJitElfPadding = 32; static constexpr uint8_t kMaxStepCount = 100; static constexpr uint8_t kMaxMapEntryCount = 50; static constexpr uint8_t kMaxBuildIdLen = 100; static constexpr uint8_t kMaxMapInfoNameLen = 150; std::unique_ptr<unwindstack::Regs> GetRegisters(unwindstack::ArchEnum arch); std::unique_ptr<unwindstack::Maps> GetMaps(FuzzedDataProvider* data_provider); std::vector<std::string> GetStringList(FuzzedDataProvider* data_provider, uint max_str_len, uint max_strings); unwindstack::ArchEnum GetArch(FuzzedDataProvider* data_provider); void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const char* name, Elf* elf = nullptr); void PutElfFilesInMemory(MemoryFake* memory, FuzzedDataProvider* data_provider); std::unique_ptr<unwindstack::DexFiles> GetDexFiles(FuzzedDataProvider* data_provider, std::shared_ptr<unwindstack::Memory> memory, uint max_libraries, uint max_library_length); #endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H
libunwindstack/tests/fuzz/UnwinderFuzz.cpp 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright 2020 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 <functional> #include <iostream> #include <vector> #include <unwindstack/JitDebug.h> #include <unwindstack/Maps.h> #include <unwindstack/Memory.h> #include <unwindstack/Unwinder.h> #include "../MemoryFake.h" #include "UnwinderComponentCreator.h" #include "fuzzer/FuzzedDataProvider.h" namespace unwindstack { static constexpr int kMaxUnwindStringLen = 50; static constexpr int kMaxUnwindStrings = 50; void PerformUnwind(FuzzedDataProvider* data_provider, Unwinder* unwinder) { // 0 = don't set any values // 1 = set initial_map_names_to_skip // 2 = set map_suffixes_to_ignore // 3 = set both uint8_t set_values = data_provider->ConsumeIntegral<uint8_t>() % 4; if (set_values == 0) { unwinder->Unwind(); } else if (set_values == 1) { // Only setting initial_map_names_to_skip std::vector<std::string> skip_names = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(&skip_names, nullptr); } else if (set_values == 2) { // Only setting map_suffixes_to_ignore std::vector<std::string> ignore_suffixes = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(nullptr, &ignore_suffixes); } else if (set_values == 3) { // Setting both values std::vector<std::string> skip_names = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); std::vector<std::string> ignore_suffixes = GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); unwinder->Unwind(&skip_names, &ignore_suffixes); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider data_provider(data, size); // We need to construct an unwinder. // Generate the Maps: std::unique_ptr<Maps> maps = GetMaps(&data_provider); // Generate the Regs: uint8_t arch_val = data_provider.ConsumeIntegralInRange<uint8_t>(1, kArchCount); ArchEnum arch = static_cast<ArchEnum>(arch_val); std::unique_ptr<Regs> regs = GetRegisters(arch); // Generate memory: std::shared_ptr<Memory> memory = std::make_shared<MemoryFake>(); PutElfFilesInMemory(reinterpret_cast<MemoryFake*>(memory.get()), &data_provider); size_t max_frames = data_provider.ConsumeIntegralInRange<size_t>(0, 5000); std::unique_ptr<JitDebug> jit_debug_ptr = std::make_unique<JitDebug>(memory); // Create instance Unwinder unwinder(max_frames, maps.get(), regs.get(), memory); unwinder.SetJitDebug(jit_debug_ptr.get(), arch); unwinder.SetResolveNames(data_provider.ConsumeBool()); // Call unwind PerformUnwind(&data_provider, &unwinder); // Run some additional logic that changes after unwind uint64_t pc = data_provider.ConsumeIntegral<uint64_t>(); unwinder.BuildFrameFromPcOnly(pc); unwinder.ConsumeFrames(); return 0; } } // namespace unwindstack