diff options
-rw-r--r-- | abs32_utils.cc | 32 | ||||
-rw-r--r-- | abs32_utils.h | 9 | ||||
-rw-r--r-- | abs32_utils_unittest.cc | 49 | ||||
-rw-r--r-- | address_translator.cc | 2 | ||||
-rw-r--r-- | address_translator.h | 3 | ||||
-rw-r--r-- | address_translator_unittest.cc | 30 | ||||
-rw-r--r-- | disassembler_elf.cc | 51 | ||||
-rw-r--r-- | disassembler_win32.cc | 20 | ||||
-rw-r--r-- | reloc_win32.cc | 2 |
9 files changed, 163 insertions, 35 deletions
diff --git a/abs32_utils.cc b/abs32_utils.cc index bf6526b..91bdb61 100644 --- a/abs32_utils.cc +++ b/abs32_utils.cc @@ -94,11 +94,11 @@ Abs32RvaExtractorWin32::Abs32RvaExtractorWin32( offset_t hi) : image_(image), addr_(std::move(addr)) { CHECK_LE(lo, hi); - auto find_and_check = [&addr](const std::vector<offset_t>& locations, - offset_t offset) { + auto find_and_check = [this](const std::vector<offset_t>& locations, + offset_t offset) { auto it = std::lower_bound(locations.begin(), locations.end(), offset); - // Ensure |offset| does not straddle a reference body. - CHECK(it == locations.begin() || offset - *(it - 1) >= addr.width()); + // Ensure that |offset| does not straddle a reference body. + CHECK(it == locations.begin() || offset - *(it - 1) >= addr_.width()); return it; }; cur_abs32_ = find_and_check(abs32_locations, lo); @@ -136,11 +136,8 @@ base::Optional<Reference> Abs32ReaderWin32::GetNext() { for (auto unit = abs32_rva_extractor_.GetNext(); unit.has_value(); unit = abs32_rva_extractor_.GetNext()) { offset_t location = unit->location; - // |target| will not be dereferenced, so we don't worry about it - // exceeding |image_.size()| (in fact, there are valid cases where it - // does). offset_t unsafe_target = target_rva_to_offset_.Convert(unit->target_rva); - if (unsafe_target < kOffsetBound) + if (unsafe_target != kInvalidOffset) return Reference{location, unsafe_target}; } return base::nullopt; @@ -167,6 +164,25 @@ void Abs32WriterWin32::PutNext(Reference ref) { /******** Exported Functions ********/ +size_t RemoveUntranslatableAbs32(ConstBufferView image, + AbsoluteAddress&& addr, + const AddressTranslator& translator, + std::vector<offset_t>* locations) { + AddressTranslator::RvaToOffsetCache target_rva_checker(translator); + Abs32RvaExtractorWin32 extractor(image, std::move(addr), *locations, 0, + image.size()); + Abs32ReaderWin32 reader(std::move(extractor), translator); + std::vector<offset_t>::iterator write_it = locations->begin(); + // |reader| reads |locations| while |write_it| modifies it. However, there's + // no conflict since read occurs before write, and can skip ahead. + for (auto ref = reader.GetNext(); ref.has_value(); ref = reader.GetNext()) + *(write_it++) = ref->location; + DCHECK(write_it <= locations->end()); + size_t num_removed = locations->end() - write_it; + locations->erase(write_it, locations->end()); + return num_removed; +} + size_t RemoveOverlappingAbs32Locations(uint32_t width, std::vector<offset_t>* locations) { if (locations->size() <= 1) diff --git a/abs32_utils.h b/abs32_utils.h index a97aa38..5bc9cf2 100644 --- a/abs32_utils.h +++ b/abs32_utils.h @@ -45,7 +45,7 @@ class AbsoluteAddress { // the write and returns true. On failure (invalid |offset|), returns false. bool Write(offset_t offset, MutableBufferView* image); - size_t width() const { return WidthOf(bitness_); } + uint32_t width() const { return WidthOf(bitness_); } // Exposing |value_| for testing. uint64_t* mutable_value() { return &value_; } @@ -126,6 +126,13 @@ class Abs32WriterWin32 : public ReferenceWriter { DISALLOW_COPY_AND_ASSIGN(Abs32WriterWin32); }; +// Given a list of abs32 |locations|, removes all elements whose targets cannot +// be translated. Returns the number of elements removed. +size_t RemoveUntranslatableAbs32(ConstBufferView image, + AbsoluteAddress&& addr, + const AddressTranslator& translator, + std::vector<offset_t>* locations); + // Given a sorted list of abs32 |locations|, removes all elements whose body // (with |width| given) overlaps with the body of a previous element. size_t RemoveOverlappingAbs32Locations(uint32_t width, diff --git a/abs32_utils_unittest.cc b/abs32_utils_unittest.cc index a8f6917..77a2599 100644 --- a/abs32_utils_unittest.cc +++ b/abs32_utils_unittest.cc @@ -11,6 +11,7 @@ #include <utility> #include "base/numerics/safe_conversions.h" +#include "components/zucchini/address_translator.h" #include "components/zucchini/image_utils.h" #include "components/zucchini/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -52,7 +53,7 @@ TEST(Abs32UtilsTest, AbsoluteAddress32) { EXPECT_TRUE(addr32.Read(0x8U, image32)); EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Underflow. EXPECT_TRUE(addr32.Read(0xCU, image32)); - EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Translated RVA would be to large. + EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Translated RVA would be too large. EXPECT_TRUE(addr32.Read(0x10U, image32)); EXPECT_EQ(kInvalidRva, addr32.ToRva()); // Underflow (boundary case). @@ -432,6 +433,52 @@ TEST(Abs32UtilsTest, Win32Write64) { EXPECT_EQ(expected_data64, data64); } +TEST(Abs32UtilsTest, RemoveUntranslatableAbs32) { + Bitness kBitness = kBit32; + uint64_t kImageBase = 0x2BCD0000; + + // Valid RVAs: [0x00001A00, 0x00001A28) and [0x00003A00, 0x00004000). + // Valid AVAs: [0x2BCD1A00, 0x2BCD1A28) and [0x2BCD3A00, 0x2BCD4000). + // Notice that the second section has has dangling RVA. + AddressTranslator translator; + ASSERT_EQ(AddressTranslator::kSuccess, + translator.Initialize( + {{0x04, +0x28, 0x1A00, +0x28}, {0x30, +0x30, 0x3A00, +0x600}})); + + std::vector<uint8_t> data = ParseHexString( + "FF FF FF FF 0B 3A CD 2B 00 00 00 04 3A CD 2B 00 " + "FC 3F CD 2B 14 1A CD 2B 44 00 00 00 CC 00 00 00 " + "00 00 55 00 00 00 1E 1A CD 2B 00 99 FF FF FF FF " + "10 3A CD 2B 22 00 00 00 00 00 00 11 00 00 00 00 " + "66 00 00 00 28 1A CD 2B 00 00 CD 2B 27 1A CD 2B " + "FF 39 CD 2B 00 00 00 00 18 1A CD 2B 00 00 00 00 " + "FF FF FF FF FF FF FF FF"); + MutableBufferView image(data.data(), data.size()); + + const offset_t kAbs1 = 0x04; // a:2BCD3A0B = r:3A0B = o:3B + const offset_t kAbs2 = 0x0B; // a:2BCD3A04 = r:3A04 = o:34 + const offset_t kAbs3 = 0x10; // a:2BCD3FFF = r:3FFF (dangling) + const offset_t kAbs4 = 0x14; // a:2BCD1A14 = r:1A14 = o:18 + const offset_t kAbs5 = 0x26; // a:2BCD1A1E = r:1A1E = o:22 + const offset_t kAbs6 = 0x30; // a:2BCD3A10 = r:3A10 = 0x40 + const offset_t kAbs7 = 0x44; // a:2BCD1A28 = r:1A28 (bad: sentinel) + const offset_t kAbs8 = 0x48; // a:2BCD0000 = r:0000 (bad: not covered) + const offset_t kAbs9 = 0x4C; // a:2BCD1A27 = r:1A27 = 0x2B + const offset_t kAbsA = 0x50; // a:2BCD39FF (bad: not covered) + const offset_t kAbsB = 0x54; // a:00000000 (bad: underflow) + const offset_t kAbsC = 0x58; // a:2BCD1A18 = r:1A18 = 0x1C + + std::vector<offset_t> locations = {kAbs1, kAbs2, kAbs3, kAbs4, kAbs5, kAbs6, + kAbs7, kAbs8, kAbs9, kAbsA, kAbsB, kAbsC}; + std::vector<offset_t> exp_locations = {kAbs1, kAbs2, kAbs3, kAbs4, + kAbs5, kAbs6, kAbs9, kAbsC}; + size_t exp_num_removed = locations.size() - exp_locations.size(); + size_t num_removed = RemoveUntranslatableAbs32(image, {kBitness, kImageBase}, + translator, &locations); + EXPECT_EQ(exp_num_removed, num_removed); + EXPECT_EQ(exp_locations, locations); +} + TEST(Abs32UtilsTest, RemoveOverlappingAbs32Locations) { // Make |width| a state to reduce repetition. uint32_t width = WidthOf(kBit32); diff --git a/address_translator.cc b/address_translator.cc index 5b388da..20f19b2 100644 --- a/address_translator.cc +++ b/address_translator.cc @@ -38,6 +38,8 @@ AddressTranslator::RvaToOffsetCache::RvaToOffsetCache( : translator_(translator) {} bool AddressTranslator::RvaToOffsetCache::IsValid(rva_t rva) const { + if (rva == kInvalidRva) + return false; if (!cached_unit_ || !cached_unit_->CoversRva(rva)) { const AddressTranslator::Unit* unit = translator_.RvaToUnit(rva); if (!unit) diff --git a/address_translator.h b/address_translator.h index 821b9ad..5666b99 100644 --- a/address_translator.h +++ b/address_translator.h @@ -59,7 +59,7 @@ namespace zucchini { using rva_t = uint32_t; // Divide by 2 to match |kOffsetBound|. constexpr rva_t kRvaBound = static_cast<rva_t>(-1) / 2; -constexpr rva_t kInvalidRva = static_cast<rva_t>(-1); +constexpr rva_t kInvalidRva = static_cast<rva_t>(-2); // A utility to translate between offsets and RVAs in an image. class AddressTranslator { @@ -133,6 +133,7 @@ class AddressTranslator { explicit RvaToOffsetCache(const AddressTranslator& translator); bool IsValid(rva_t rva) const; + offset_t Convert(rva_t rva) const; private: diff --git a/address_translator_unittest.cc b/address_translator_unittest.cc index 0aeff77..efa2f14 100644 --- a/address_translator_unittest.cc +++ b/address_translator_unittest.cc @@ -553,4 +553,34 @@ TEST(AddressTranslatorTest, Merge) { } while (std::next_permutation(test_case2.begin(), test_case2.end())); } +TEST(AddressTranslatorTest, RvaToOffsetCache_IsValid) { + AddressTranslator translator; + // Notice that the second section has dangling RVA. + ASSERT_EQ(AddressTranslator::kSuccess, + translator.Initialize( + {{0x04, +0x28, 0x1A00, +0x28}, {0x30, +0x10, 0x3A00, +0x30}})); + AddressTranslator::RvaToOffsetCache rva_checker(translator); + + EXPECT_FALSE(rva_checker.IsValid(kInvalidRva)); + + for (int i = 0; i < 0x28; ++i) + EXPECT_TRUE(rva_checker.IsValid(0x1A00 + i)); + EXPECT_FALSE(rva_checker.IsValid(0x1A00 + 0x28)); + EXPECT_FALSE(rva_checker.IsValid(0x1A00 + 0x29)); + EXPECT_FALSE(rva_checker.IsValid(0x1A00 - 1)); + EXPECT_FALSE(rva_checker.IsValid(0x1A00 - 2)); + + for (int i = 0; i < 0x30; ++i) + EXPECT_TRUE(rva_checker.IsValid(0x3A00 + i)); + EXPECT_FALSE(rva_checker.IsValid(0x3A00 + 0x30)); + EXPECT_FALSE(rva_checker.IsValid(0x3A00 + 0x31)); + EXPECT_FALSE(rva_checker.IsValid(0x3A00 - 1)); + EXPECT_FALSE(rva_checker.IsValid(0x3A00 - 2)); + + EXPECT_FALSE(rva_checker.IsValid(0)); + EXPECT_FALSE(rva_checker.IsValid(0x10)); + EXPECT_FALSE(rva_checker.IsValid(0x7FFFFFFFU)); + EXPECT_FALSE(rva_checker.IsValid(0xFFFFFFFFU)); +} + } // namespace zucchini diff --git a/disassembler_elf.cc b/disassembler_elf.cc index 0f6d3a0..be01c4c 100644 --- a/disassembler_elf.cc +++ b/disassembler_elf.cc @@ -19,6 +19,8 @@ namespace zucchini { namespace { +constexpr uint64_t kElfImageBase = 0; + // Determines whether |section| is a reloc section. template <class Traits> bool IsRelocSection(const typename Traits::Elf_Shdr& section) { @@ -288,23 +290,33 @@ template <class Traits> void DisassemblerElf<Traits>::GetAbs32FromRelocSections() { constexpr int kAbs32Width = Traits::kVAWidth; DCHECK(abs32_locations_.empty()); - auto relocs = MakeReadRelocs(0, offset_t(size())); - for (auto ref = relocs->GetNext(); ref; ref = relocs->GetNext()) { - // Reject null targets and targets outside |image_|. Note that here we - // assume abs32 targets are never "fake offsets". - if (ref->target > 0 && image_.covers({ref->target, kAbs32Width})) - abs32_locations_.push_back(ref->target); - } - abs32_locations_.shrink_to_fit(); + + // Read reloc targets as preliminary abs32 locations. + std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size())); + for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext()) + abs32_locations_.push_back(ref->target); + std::sort(abs32_locations_.begin(), abs32_locations_.end()); + // Abs32 references must have targets translatable to offsets. Remove those + // that are unable to do so. + // TODO(huangs): Investigate whether passing |Traits::kBitness| is correct: + // Some architectures using ELF might have 4-byte long abs32 body regardless + // of bitness. + size_t num_untranslatable = + RemoveUntranslatableAbs32(image_, {Traits::kBitness, kElfImageBase}, + translator_, &abs32_locations_); + LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable + << " untranslatable abs32 references."; + // Abs32 reference bodies must not overlap. If found, simply remove them. - size_t num_removed = + size_t num_overlapping = RemoveOverlappingAbs32Locations(kAbs32Width, &abs32_locations_); - if (num_removed) { - LOG(WARNING) << "Warning: Found and removed " << num_removed - << " abs32 locations with overlapping bodies."; - } + LOG_IF(WARNING, num_overlapping) + << "Removed " << num_overlapping + << " abs32 references with overlapping bodies."; + + abs32_locations_.shrink_to_fit(); } template <class Traits> @@ -392,9 +404,12 @@ template <class Traits> std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadAbs32( offset_t lo, offset_t hi) { - Abs32RvaExtractorWin32 abs_rva_extractor(this->image_, - AbsoluteAddress(Traits::kBitness, 0), - this->abs32_locations_, lo, hi); + // TODO(huangs): Don't use Abs32RvaExtractorWin32 here; use new class that + // caters to different ELF architectures (e.g., abs32 in AArch64 are 4 bytes + // long, not 8 bytes long). + Abs32RvaExtractorWin32 abs_rva_extractor( + this->image_, AbsoluteAddress(Traits::kBitness, kElfImageBase), + this->abs32_locations_, lo, hi); return std::make_unique<Abs32ReaderWin32>(std::move(abs_rva_extractor), this->translator_); } @@ -402,8 +417,10 @@ std::unique_ptr<ReferenceReader> DisassemblerElfIntel<Traits>::MakeReadAbs32( template <class Traits> std::unique_ptr<ReferenceWriter> DisassemblerElfIntel<Traits>::MakeWriteAbs32( MutableBufferView image) { + // TODO(huangs): For AArch64, see if |Traits::kBitness| should be used here? return std::make_unique<Abs32WriterWin32>( - image, AbsoluteAddress(Traits::kBitness, 0), this->translator_); + image, AbsoluteAddress(Traits::kBitness, kElfImageBase), + this->translator_); } template <class Traits> diff --git a/disassembler_win32.cc b/disassembler_win32.cc index b3f568b..9067ad7 100644 --- a/disassembler_win32.cc +++ b/disassembler_win32.cc @@ -316,20 +316,28 @@ bool DisassemblerWin32<Traits>::ParseAndStoreAbs32() { return true; has_parsed_abs32_ = true; - ParseAndStoreRelocBlocks(); - + // Read reloc targets as preliminary abs32 locations. std::unique_ptr<ReferenceReader> relocs = MakeReadRelocs(0, offset_t(size())); for (auto ref = relocs->GetNext(); ref.has_value(); ref = relocs->GetNext()) abs32_locations_.push_back(ref->target); - abs32_locations_.shrink_to_fit(); std::sort(abs32_locations_.begin(), abs32_locations_.end()); + // Abs32 references must have targets translatable to offsets. Remove those + // that are unable to do so. + size_t num_untranslatable = RemoveUntranslatableAbs32( + image_, {Traits::kBitness, image_base_}, translator_, &abs32_locations_); + LOG_IF(WARNING, num_untranslatable) << "Removed " << num_untranslatable + << " untranslatable abs32 references."; + // Abs32 reference bodies must not overlap. If found, simply remove them. - size_t num_removed = + size_t num_overlapping = RemoveOverlappingAbs32Locations(Traits::kVAWidth, &abs32_locations_); - LOG_IF(WARNING, num_removed) << "Found and removed " << num_removed - << " abs32 locations with overlapping bodies."; + LOG_IF(WARNING, num_overlapping) + << "Removed " << num_overlapping + << " abs32 references with overlapping bodies."; + + abs32_locations_.shrink_to_fit(); return true; } diff --git a/reloc_win32.cc b/reloc_win32.cc index dcf40bb..6da48f2 100644 --- a/reloc_win32.cc +++ b/reloc_win32.cc @@ -152,7 +152,7 @@ base::Optional<Reference> RelocReaderWin32::GetNext() { offset_t target = entry_rva_to_offset_.Convert(unit->target_rva); if (target == kInvalidOffset) continue; - // Ensures the target (abs32 reference) lies entirely within the image. + // Ensure that |target| (abs32 reference) lies entirely within the image. if (target >= offset_bound_) continue; offset_t location = unit->location; |