1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/zucchini/reloc_win32.h"
6 
7 #include <algorithm>
8 #include <tuple>
9 #include <utility>
10 
11 #include "base/logging.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "components/zucchini/algorithm.h"
14 #include "components/zucchini/io_utils.h"
15 #include "components/zucchini/type_win_pe.h"
16 
17 namespace zucchini {
18 
19 /******** RelocUnitWin32 ********/
20 
21 RelocUnitWin32::RelocUnitWin32() = default;
RelocUnitWin32(uint8_t type_in,offset_t location_in,rva_t target_rva_in)22 RelocUnitWin32::RelocUnitWin32(uint8_t type_in,
23                                offset_t location_in,
24                                rva_t target_rva_in)
25     : type(type_in), location(location_in), target_rva(target_rva_in) {}
26 
operator ==(const RelocUnitWin32 & a,const RelocUnitWin32 & b)27 bool operator==(const RelocUnitWin32& a, const RelocUnitWin32& b) {
28   return std::tie(a.type, a.location, a.target_rva) ==
29          std::tie(b.type, b.location, b.target_rva);
30 }
31 
32 /******** RelocRvaReaderWin32 ********/
33 
34 // static
FindRelocBlocks(ConstBufferView image,BufferRegion reloc_region,std::vector<offset_t> * reloc_block_offsets)35 bool RelocRvaReaderWin32::FindRelocBlocks(
36     ConstBufferView image,
37     BufferRegion reloc_region,
38     std::vector<offset_t>* reloc_block_offsets) {
39   CHECK_LT(reloc_region.size, kOffsetBound);
40   ConstBufferView reloc_data = image[reloc_region];
41   reloc_block_offsets->clear();
42   while (reloc_data.size() >= sizeof(pe::RelocHeader)) {
43     reloc_block_offsets->push_back(
44         base::checked_cast<offset_t>(reloc_data.begin() - image.begin()));
45     auto size = reloc_data.read<pe::RelocHeader>(0).size;
46     // |size| must be aligned to 4-bytes.
47     if (size < sizeof(pe::RelocHeader) || size % 4 || size > reloc_data.size())
48       return false;
49     reloc_data.remove_prefix(size);
50   }
51   return reloc_data.empty();  // Fail if trailing data exist.
52 }
53 
RelocRvaReaderWin32(ConstBufferView image,BufferRegion reloc_region,const std::vector<offset_t> & reloc_block_offsets,offset_t lo,offset_t hi)54 RelocRvaReaderWin32::RelocRvaReaderWin32(
55     ConstBufferView image,
56     BufferRegion reloc_region,
57     const std::vector<offset_t>& reloc_block_offsets,
58     offset_t lo,
59     offset_t hi)
60     : image_(image) {
61   CHECK_LE(lo, hi);
62   lo = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(lo));
63   hi = base::checked_cast<offset_t>(reloc_region.InclusiveClamp(hi));
64   end_it_ = image_.begin() + hi;
65 
66   // By default, get GetNext() to produce empty output.
67   cur_reloc_units_ = BufferSource(end_it_, 0);
68   if (reloc_block_offsets.empty())
69     return;
70 
71   // Find the block that contains |lo|.
72   auto block_it = std::upper_bound(reloc_block_offsets.begin(),
73                                    reloc_block_offsets.end(), lo);
74   DCHECK(block_it != reloc_block_offsets.begin());
75   --block_it;
76 
77   // Initialize |cur_reloc_units_| and |rva_hi_bits_|.
78   if (!LoadRelocBlock(image_.begin() + *block_it))
79     return;  // Nothing left.
80 
81   // Skip |cur_reloc_units_| to |lo|, truncating up.
82   offset_t cur_reloc_units_offset =
83       base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
84   if (lo > cur_reloc_units_offset) {
85     offset_t delta =
86         AlignCeil<offset_t>(lo - cur_reloc_units_offset, kRelocUnitSize);
87     cur_reloc_units_.Skip(delta);
88   }
89 }
90 
91 RelocRvaReaderWin32::RelocRvaReaderWin32(RelocRvaReaderWin32&&) = default;
92 
93 RelocRvaReaderWin32::~RelocRvaReaderWin32() = default;
94 
95 // Unrolls a nested loop: outer = reloc blocks and inner = reloc entries.
GetNext()96 base::Optional<RelocUnitWin32> RelocRvaReaderWin32::GetNext() {
97   // "Outer loop" to find non-empty reloc block.
98   while (cur_reloc_units_.Remaining() < kRelocUnitSize) {
99     if (!LoadRelocBlock(cur_reloc_units_.end()))
100       return base::nullopt;
101   }
102   if (end_it_ - cur_reloc_units_.begin() < kRelocUnitSize)
103     return base::nullopt;
104   // "Inner loop" to extract single reloc unit.
105   offset_t location =
106       base::checked_cast<offset_t>(cur_reloc_units_.begin() - image_.begin());
107   uint16_t entry = cur_reloc_units_.read<uint16_t>(0);
108   uint8_t type = static_cast<uint8_t>(entry >> 12);
109   rva_t rva = rva_hi_bits_ + (entry & 0xFFF);
110   cur_reloc_units_.Skip(kRelocUnitSize);
111   return RelocUnitWin32{type, location, rva};
112 }
113 
LoadRelocBlock(ConstBufferView::const_iterator block_begin)114 bool RelocRvaReaderWin32::LoadRelocBlock(
115     ConstBufferView::const_iterator block_begin) {
116   ConstBufferView header_buf(block_begin, sizeof(pe::RelocHeader));
117   if (header_buf.end() >= end_it_ ||
118       end_it_ - header_buf.end() < kRelocUnitSize) {
119     return false;
120   }
121   const auto& header = header_buf.read<pe::RelocHeader>(0);
122   rva_hi_bits_ = header.rva_hi;
123   uint32_t block_size = header.size;
124   if (block_size < sizeof(pe::RelocHeader))
125     return false;
126   if ((block_size - sizeof(pe::RelocHeader)) % kRelocUnitSize != 0)
127     return false;
128   cur_reloc_units_ = BufferSource(block_begin, block_size);
129   cur_reloc_units_.Skip(sizeof(pe::RelocHeader));
130   return true;
131 }
132 
133 /******** RelocReaderWin32 ********/
134 
RelocReaderWin32(RelocRvaReaderWin32 && reloc_rva_reader,uint16_t reloc_type,offset_t offset_bound,const AddressTranslator & translator)135 RelocReaderWin32::RelocReaderWin32(RelocRvaReaderWin32&& reloc_rva_reader,
136                                    uint16_t reloc_type,
137                                    offset_t offset_bound,
138                                    const AddressTranslator& translator)
139     : reloc_rva_reader_(std::move(reloc_rva_reader)),
140       reloc_type_(reloc_type),
141       offset_bound_(offset_bound),
142       entry_rva_to_offset_(translator) {}
143 
144 RelocReaderWin32::~RelocReaderWin32() = default;
145 
146 // ReferenceReader:
GetNext()147 base::Optional<Reference> RelocReaderWin32::GetNext() {
148   for (base::Optional<RelocUnitWin32> unit = reloc_rva_reader_.GetNext();
149        unit.has_value(); unit = reloc_rva_reader_.GetNext()) {
150     if (unit->type != reloc_type_)
151       continue;
152     offset_t target = entry_rva_to_offset_.Convert(unit->target_rva);
153     if (target == kInvalidOffset)
154       continue;
155     // Ensure that |target| (abs32 reference) lies entirely within the image.
156     if (target >= offset_bound_)
157       continue;
158     offset_t location = unit->location;
159     return Reference{location, target};
160   }
161   return base::nullopt;
162 }
163 
164 /******** RelocWriterWin32 ********/
165 
RelocWriterWin32(uint16_t reloc_type,MutableBufferView image,BufferRegion reloc_region,const std::vector<offset_t> & reloc_block_offsets,const AddressTranslator & translator)166 RelocWriterWin32::RelocWriterWin32(
167     uint16_t reloc_type,
168     MutableBufferView image,
169     BufferRegion reloc_region,
170     const std::vector<offset_t>& reloc_block_offsets,
171     const AddressTranslator& translator)
172     : reloc_type_(reloc_type),
173       image_(image),
174       reloc_region_(reloc_region),
175       reloc_block_offsets_(reloc_block_offsets),
176       target_offset_to_rva_(translator) {}
177 
178 RelocWriterWin32::~RelocWriterWin32() = default;
179 
PutNext(Reference ref)180 void RelocWriterWin32::PutNext(Reference ref) {
181   DCHECK_GE(ref.location, reloc_region_.lo());
182   DCHECK_LT(ref.location, reloc_region_.hi());
183   auto block_it = std::upper_bound(reloc_block_offsets_.begin(),
184                                    reloc_block_offsets_.end(), ref.location);
185   --block_it;
186   rva_t rva_hi_bits = image_.read<pe::RelocHeader>(*block_it).rva_hi;
187   rva_t target_rva = target_offset_to_rva_.Convert(ref.target);
188   rva_t rva_lo_bits = (target_rva - rva_hi_bits) & 0xFFF;
189   if (target_rva != rva_hi_bits + rva_lo_bits) {
190     LOG(ERROR) << "Invalid RVA at " << AsHex<8>(ref.location) << ".";
191     return;
192   }
193   image_.write<uint16_t>(ref.location, rva_lo_bits | (reloc_type_ << 12));
194 }
195 
196 }  // namespace zucchini
197