diff options
Diffstat (limited to 'disassembler_elf.cc')
-rw-r--r-- | disassembler_elf.cc | 144 |
1 files changed, 93 insertions, 51 deletions
diff --git a/disassembler_elf.cc b/disassembler_elf.cc index 2405374..fdc104f 100644 --- a/disassembler_elf.cc +++ b/disassembler_elf.cc @@ -20,12 +20,71 @@ namespace zucchini { namespace { constexpr uint64_t kElfImageBase = 0; +constexpr size_t kSizeBound = 0x7FFF0000; + +// Bit fields for JudgeSection() return value. +enum SectionJudgement : int { + // Bit: Section does not invalidate ELF, but may or may not be useful. + SECTION_BIT_SAFE = 1 << 0, + // Bit: Section useful for AddressTranslator, to map between offsets and RVAs. + SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR = 1 << 1, + // Bit: Section useful for |offset_bound|, to estimate ELF size. + SECTION_BIT_USEFUL_FOR_OFFSET_BOUND = 1 << 2, + // Bit: Section potentially useful for pointer extraction. + SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS = 1 << 3, + + // The following are verdicts from combining bits, to improve semantics. + // Default value: A section is malformed and invalidates ELF. + SECTION_IS_MALFORMED = 0, + // Section does not invalidate ELF, but is also not used for anything. + SECTION_IS_USELESS = SECTION_BIT_SAFE, +}; + +// Decides how a section affects ELF parsing, and returns a bit field composed +// from SectionJudgement values. +template <class Traits> +int JudgeSection(size_t image_size, const typename Traits::Elf_Shdr* section) { + // Examine RVA range: Reject if numerical overflow may happen. + if (!BufferRegion{section->sh_addr, section->sh_size}.FitsIn(kSizeBound)) + return SECTION_IS_MALFORMED; + + // Examine offset range: If section takes up |image| data then be stricter. + size_t offset_bound = + (section->sh_type == elf::SHT_NOBITS) ? kSizeBound : image_size; + if (!BufferRegion{section->sh_offset, section->sh_size}.FitsIn(offset_bound)) + return SECTION_IS_MALFORMED; + + // Empty sections don't contribute to offset-RVA mapping. For consistency, it + // should also not affect |offset_bounds|. + if (section->sh_size == 0) + return SECTION_IS_USELESS; + + // Sections with |sh_addr == 0| are ignored because these tend to duplicates + // (can cause problems for lookup) and uninteresting. For consistency, it + // should also not affect |offset_bounds|. + if (section->sh_addr == 0) + return SECTION_IS_USELESS; + + if (section->sh_type == elf::SHT_NOBITS) { + // Special case for .tbss sections: These should be ignored because they may + // have offset-RVA map that don't match other sections. + if (section->sh_flags & elf::SHF_TLS) + return SECTION_IS_USELESS; + + // Section is useful for offset-RVA translation, but does not affect + // |offset_bounds| since it can have large virtual size (e.g., .bss). + return SECTION_BIT_SAFE | SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR; + } + + return SECTION_BIT_SAFE | SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR | + SECTION_BIT_USEFUL_FOR_OFFSET_BOUND | + SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS; +} // Determines whether |section| is a reloc section. template <class Traits> bool IsRelocSection(const typename Traits::Elf_Shdr& section) { - if (section.sh_size == 0) - return false; + DCHECK_GT(section.sh_size, 0U); if (section.sh_type == elf::SHT_REL) { // Also validate |section.sh_entsize|, which gets used later. return section.sh_entsize == sizeof(typename Traits::Elf_Rel); @@ -38,7 +97,9 @@ bool IsRelocSection(const typename Traits::Elf_Shdr& section) { // Determines whether |section| is a section with executable code. template <class Traits> bool IsExecSection(const typename Traits::Elf_Shdr& section) { - return (section.sh_flags & elf::SHF_EXECINSTR) != 0; + DCHECK_GT(section.sh_size, 0U); + return section.sh_type == elf::SHT_PROGBITS && + (section.sh_flags & elf::SHF_EXECINSTR) != 0; } } // namespace @@ -199,48 +260,39 @@ bool DisassemblerElf<Traits>::ParseHeader() { // Establish bound on encountered offsets. offset_t offset_bound = std::max(section_table_end, segment_table_end); - // Visit each section, validate, and add address translation data to |units|. + // Visits |segments_| to get estimate on |offset_bound|. + for (const typename Traits::Elf_Phdr* segment = segments_; + segment != segments_ + segments_count_; ++segment) { + if (!image_.covers({segment->p_offset, segment->p_filesz})) + return false; + offset_t segment_end = + base::checked_cast<offset_t>(segment->p_offset + segment->p_filesz); + offset_bound = std::max(offset_bound, segment_end); + } + + // Visit and validate each section; add address translation data to |units|. std::vector<AddressTranslator::Unit> units; units.reserve(sections_count_); + section_judgements_.reserve(sections_count_); for (int i = 0; i < sections_count_; ++i) { const typename Traits::Elf_Shdr* section = §ions_[i]; + int judgement = JudgeSection<Traits>(image_.size(), section); + section_judgements_.push_back(judgement); + if ((judgement & SECTION_BIT_SAFE) == 0) + return false; - // Skip empty sections. These don't affect |offset_bound|, and don't - // contribute to RVA-offset mapping. - if (section->sh_size == 0) { - // Skipping empty sections is only safe if the |sh_offset| is within the - // image. Fail if this is not true as the input is ill-formed. - if (section->sh_offset >= image_.size()) - return false; - - continue; - } - - // Extract dimensions to 32-bit integers to facilitate conversion. Range of - // values was ensured above when checking that the section is bounded. uint32_t sh_size = base::checked_cast<uint32_t>(section->sh_size); offset_t sh_offset = base::checked_cast<offset_t>(section->sh_offset); rva_t sh_addr = base::checked_cast<rva_t>(section->sh_addr); - - // Update |offset_bound|. - if (section->sh_type != elf::SHT_NOBITS) { - // Be strict with offsets: Any size overflow invalidates the file. - if (!image_.covers({sh_offset, sh_size})) - return false; - - offset_t section_end = sh_offset + sh_size; - offset_bound = std::max(offset_bound, section_end); - } - - // Compute mappings to translate between RVA and offset. As a heuristic, - // sections with RVA == 0 (i.e., |sh_addr == 0|) are ignored because these - // tend to be duplicates (which cause problems during lookup), and tend to - // be uninteresting. - if (section->sh_addr > 0) { - // Add |section| data for offset-RVA translation. + if ((judgement & SECTION_BIT_USEFUL_FOR_ADDRESS_TRANSLATOR) != 0) { + // Store mappings between RVA and offset. units.push_back({sh_offset, sh_size, sh_addr, sh_size}); } + if ((judgement & SECTION_BIT_USEFUL_FOR_OFFSET_BOUND) != 0) { + offset_t section_end = base::checked_cast<offset_t>(sh_offset + sh_size); + offset_bound = std::max(offset_bound, section_end); + } } // Initialize |translator_| for offset-RVA translations. Any inconsistency @@ -248,20 +300,8 @@ bool DisassemblerElf<Traits>::ParseHeader() { if (translator_.Initialize(std::move(units)) != AddressTranslator::kSuccess) return false; - // Visits |segments_| to get better estimate on |offset_bound|. - for (const typename Traits::Elf_Phdr* segment = segments_; - segment != segments_ + segments_count_; ++segment) { - if (!RangeIsBounded(segment->p_offset, segment->p_filesz, kOffsetBound)) - return false; - offset_t segment_end = - base::checked_cast<offset_t>(segment->p_offset + segment->p_filesz); - offset_bound = std::max(offset_bound, segment_end); - } - - if (offset_bound > image_.size()) - return false; + DCHECK_LE(offset_bound, image_.size()); image_.shrink(offset_bound); - return true; } @@ -271,10 +311,12 @@ void DisassemblerElf<Traits>::ExtractInterestingSectionHeaders() { DCHECK(exec_headers_.empty()); for (elf::Elf32_Half i = 0; i < sections_count_; ++i) { const typename Traits::Elf_Shdr* section = sections_ + i; - if (IsRelocSection<Traits>(*section)) - reloc_section_dims_.emplace_back(*section); - else if (IsExecSection<Traits>(*section)) - exec_headers_.push_back(section); + if ((section_judgements_[i] & SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS) != 0) { + if (IsRelocSection<Traits>(*section)) + reloc_section_dims_.emplace_back(*section); + else if (IsExecSection<Traits>(*section)) + exec_headers_.push_back(section); + } } auto comp = [](const typename Traits::Elf_Shdr* a, const typename Traits::Elf_Shdr* b) { |