diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 19c22c849..8cc780acd 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -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 //------------------------------------------------------------------------- @@ -458,3 +489,4 @@ cc_binary_host { "tests/GenGnuDebugdata.cpp", ], } + diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp new file mode 100644 index 000000000..94f5a73cd --- /dev/null +++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp @@ -0,0 +1,356 @@ +/* + * 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 GetRegisters(ArchEnum arch) { + switch (arch) { + case unwindstack::ARCH_ARM: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_ARM64: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_X86: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_X86_64: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_MIPS: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_MIPS64: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + case unwindstack::ARCH_UNKNOWN: + default: { + std::unique_ptr regs = std::make_unique(); + return regs; + } + } +} + +ArchEnum GetArch(FuzzedDataProvider* data_provider) { + uint8_t arch = data_provider->ConsumeIntegralInRange(1, kArchCount); + return static_cast(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(-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(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()); + } else { + ElfInterfaceFake::FakePushFunctionData(FunctionData(func_name, i)); + } + } +} +void ElfPushFakeStepData(FuzzedDataProvider* data_provider) { + uint8_t step_count = data_provider->ConsumeIntegralInRange(0, kMaxStepCount); + for (uint8_t i = 0; i < step_count; i++) { + uint64_t pc = data_provider->ConsumeIntegral(); + uint64_t sp = data_provider->ConsumeIntegral(); + 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()); + + ElfPushFakeFunctionData(data_provider, interface_fake); + ElfPushFakeStepData(data_provider); + + elf->FakeSetInterface(interface_fake); + ElfInterfaceFake::FakeClear(); + return elf; +} + +std::unique_ptr GetMaps(FuzzedDataProvider* data_provider) { + std::unique_ptr maps = std::make_unique(); + uint8_t entry_count = data_provider->ConsumeIntegralInRange(0, kMaxMapEntryCount); + for (uint8_t i = 0; i < entry_count; i++) { + uint64_t start = data_provider->ConsumeIntegral(); + uint64_t end = data_provider->ConsumeIntegralInRange(start, UINT64_MAX); + uint64_t offset = data_provider->ConsumeIntegral(); + 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 +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(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(); + // 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(); + // Same here, EM_NUM is 253, max valid machine type is 252 + uint8_t machine_type = data_provider->ConsumeIntegral(); + uint32_t pc = data_provider->ConsumeIntegral(); + uint32_t size = data_provider->ConsumeIntegral(); + bool sixty_four_bit = data_provider->ConsumeBool(); + bool write_jit = data_provider->ConsumeBool(); + if (sixty_four_bit) { + PutElfInMemory(memory, offset, class_type, machine_type, pc, size); + } else { + PutElfInMemory(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 GetStringList(FuzzedDataProvider* data_provider, uint max_str_len, + uint max_strings) { + uint str_count = data_provider->ConsumeIntegralInRange(0, max_strings); + std::vector strings; + for (uint i = 0; i < str_count; i++) { + strings.push_back(data_provider->ConsumeRandomLengthString(max_str_len)); + } + return strings; +} + +std::unique_ptr GetDexFiles(FuzzedDataProvider* data_provider, + std::shared_ptr memory, uint max_library_length, + uint max_libraries) { + std::vector search_libs = + GetStringList(data_provider, max_library_length, max_libraries); + if (search_libs.size() <= 0) { + return std::make_unique(memory); + } + + return std::make_unique(memory, search_libs); +} diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.h b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h new file mode 100644 index 000000000..09b3379a2 --- /dev/null +++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.h @@ -0,0 +1,83 @@ +/* + * 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 +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 GetRegisters(unwindstack::ArchEnum arch); +std::unique_ptr GetMaps(FuzzedDataProvider* data_provider); +std::vector 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 GetDexFiles(FuzzedDataProvider* data_provider, + std::shared_ptr memory, + uint max_libraries, uint max_library_length); +#endif // _LIBUNWINDSTACK_UNWINDERCOMPONENTCREATOR_H diff --git a/libunwindstack/tests/fuzz/UnwinderFuzz.cpp b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp new file mode 100644 index 000000000..2f4986a19 --- /dev/null +++ b/libunwindstack/tests/fuzz/UnwinderFuzz.cpp @@ -0,0 +1,99 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include + +#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() % 4; + if (set_values == 0) { + unwinder->Unwind(); + } else if (set_values == 1) { + // Only setting initial_map_names_to_skip + std::vector 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 ignore_suffixes = + GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); + + unwinder->Unwind(nullptr, &ignore_suffixes); + } else if (set_values == 3) { + // Setting both values + std::vector skip_names = + GetStringList(data_provider, kMaxUnwindStringLen, kMaxUnwindStrings); + std::vector 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 = GetMaps(&data_provider); + + // Generate the Regs: + uint8_t arch_val = data_provider.ConsumeIntegralInRange(1, kArchCount); + ArchEnum arch = static_cast(arch_val); + std::unique_ptr regs = GetRegisters(arch); + + // Generate memory: + std::shared_ptr memory = std::make_shared(); + PutElfFilesInMemory(reinterpret_cast(memory.get()), &data_provider); + + size_t max_frames = data_provider.ConsumeIntegralInRange(0, 5000); + + std::unique_ptr jit_debug_ptr = std::make_unique(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(); + unwinder.BuildFrameFromPcOnly(pc); + unwinder.ConsumeFrames(); + return 0; +} +} // namespace unwindstack