aboutsummaryrefslogtreecommitdiff
path: root/abs32_utils_unittest.cc
diff options
context:
space:
mode:
authorSamuel Huang <huangs@chromium.org>2018-10-10 15:48:10 +0000
committerCopybara-Service <copybara-worker@google.com>2021-07-25 20:38:54 -0700
commit98dd0173969ff0905b49bf6236d51f69c5c05f65 (patch)
treed82ee350388331a379d0324f5e60439a0e9d2208 /abs32_utils_unittest.cc
parente53806a25b988acd67fe57b42eaa611f2ba96a75 (diff)
downloadzucchini-98dd0173969ff0905b49bf6236d51f69c5c05f65.tar.gz
[Zucchini] Fix patch apply failure from untranslatable abs32 references.
Disassembler (Win32 and ELF) uses reloc to find abs32 locations, which are stored in (1) |abs32_locations_|. Later on, (1) is filtered to generate (2) actual abs32 locations. A patching failure case was discovered: Equivalence blocks must never cut across reference boundaries. However, it turns out blocks generated using (2) were subject to checks using (1), which triggers a failure since (1) - (2) is nonempty. One way for (1) != (2) to happen is from CURRENT_MODULE() usage, which creates an abs32 reference outside a section. This results in an abs32 target whose RVA does not map to an offset using section data, and gets rejected by filtering logic for (2). Fix: Apply the filtering logic direcly to (1), so (1) == (2). Details: * Add RemoveUntranslatableAbs32() (abs32_utils.h), which uses the filtering logic for (2) to preemptively remove problematic RVAs from (1), so |abs32_locations_| matches (2). Extensive unit tests are added. * DisassemblerWin32<Traits>::ParseAndStoreAbs32(): Initialize |abs32_locations_| with 3 steps: Naive extraction from relocs, RemoveUntranslatableAbs32(), and RemoveOverlappingAbs32Locations(). * DisassemblerElf<Traits>::GetAbs32FromRelocSections(): Do the same, noting that ELF's image base is always 0. Additional fixes: * address_translator.h: kInvalidRva was -1, but it should be -2 to better match kInvalidOffset. * Abs32RvaExtractorWin32::Abs32RvaExtractorWin32: The lambda |find_and_check| binds |addr|, which has been std::move()'ed. Better to just bind |this| and use |addr_|. Bug: 892284 Change-Id: I628f4668ea231c7e06f35bd924652ca4d74bb848 Reviewed-on: https://chromium-review.googlesource.com/c/1263877 Reviewed-by: Greg Thompson <grt@chromium.org> Reviewed-by: Samuel Huang <huangs@chromium.org> Commit-Queue: Samuel Huang <huangs@chromium.org> Cr-Commit-Position: refs/heads/master@{#598342} NOKEYCHECK=True GitOrigin-RevId: b6d108f1cabab2a9f3fe46a7cdeb92685a2c790e
Diffstat (limited to 'abs32_utils_unittest.cc')
-rw-r--r--abs32_utils_unittest.cc49
1 files changed, 48 insertions, 1 deletions
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);