1// Copyright 2013 Google Inc. All rights reserved.
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are
5// met:
6//
7//     * Redistributions of source code must retain the above copyright
8// notice, this list of conditions and the following disclaimer.
9//     * Redistributions in binary form must reproduce the above
10// copyright notice, this list of conditions and the following disclaimer
11// in the documentation and/or other materials provided with the
12// distribution.
13//     * Neither the name of Google Inc. nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29// Unittests for OMAP related functions.
30
31#include "common/windows/omap.h"
32
33#include "gmock/gmock.h"
34#include "gtest/gtest.h"
35
36namespace google_breakpad {
37
38// Equality operators for ContainerEq. These must be outside of the anonymous
39// namespace in order for them to be found.
40bool operator==(const MappedRange& mr1, const MappedRange& mr2) {
41  return mr1.rva_original == mr2.rva_original &&
42      mr1.rva_transformed == mr2.rva_transformed &&
43      mr1.length == mr2.length &&
44      mr1.injected == mr2.injected &&
45      mr1.removed == mr2.removed;
46}
47bool operator==(const EndpointIndex& ei1, const EndpointIndex& ei2) {
48  return ei1.endpoint == ei2.endpoint && ei1.index == ei2.index;
49}
50
51// Pretty printers for more meaningful error messages. Also need to be outside
52// the anonymous namespace.
53std::ostream& operator<<(std::ostream& os, const MappedRange& mr) {
54  os << "MappedRange(rva_original=" << mr.rva_original
55     << ", rva_transformed=" << mr.rva_transformed
56     << ", length=" << mr.length
57     << ", injected=" << mr.injected
58     << ", removed=" << mr.removed << ")";
59  return os;
60}
61std::ostream& operator<<(std::ostream& os, const EndpointIndex& ei) {
62  os << "EndpointIndex(endpoint=" << ei.endpoint
63     << ", index=" << ei.index << ")";
64  return os;
65}
66std::ostream& operator<<(std::ostream& os, const AddressRange& ar) {
67  os << "AddressRange(rva=" << ar.rva << ", length=" << ar.length << ")";
68  return os;
69}
70
71namespace {
72
73OMAP CreateOmap(DWORD rva, DWORD rvaTo) {
74  OMAP o = { rva, rvaTo };
75  return o;
76}
77
78MappedRange CreateMappedRange(DWORD rva_original,
79                              DWORD rva_transformed,
80                              DWORD length,
81                              DWORD injected,
82                              DWORD removed) {
83  MappedRange mr = { rva_original, rva_transformed, length, injected, removed };
84  return mr;
85}
86
87EndpointIndex CreateEndpointIndex(DWORD endpoint, size_t index) {
88  EndpointIndex ei = { endpoint, index };
89  return ei;
90}
91
92//              (C is removed)
93// Original   :  A B C D E F G H
94// Transformed:  A B D F E * H1 G1 G2 H2
95//              (* is injected, G is copied, H is split)
96// A is implied.
97
98// Layout of the original image.
99const AddressRange B(100, 15);
100const AddressRange C(B.end(), 10);
101const AddressRange D(C.end(), 25);
102const AddressRange E(D.end(), 10);
103const AddressRange F(E.end(), 40);
104const AddressRange G(F.end(), 3);
105const AddressRange H(G.end(), 7);
106
107// Layout of the transformed image.
108const AddressRange Bt(100, 15);
109const AddressRange Dt(Bt.end(), 20);  // D is shortened.
110const AddressRange Ft(Dt.end(), F.length);
111const AddressRange Et(Ft.end(), E.length);
112const AddressRange injected(Et.end(), 5);
113const AddressRange H1t(injected.end(), 4);  // H is split.
114const AddressRange G1t(H1t.end(), G.length);  // G is copied.
115const AddressRange G2t(G1t.end(), G.length);  // G is copied.
116const AddressRange H2t(G2t.end(), 3);  // H is split.
117
118class BuildImageMapTest : public testing::Test {
119 public:
120  static const DWORD kInvalidAddress = 0xFFFFFFFF;
121
122  void InitOmapData() {
123    omap_data.length_original = H.end();
124
125    // Build the OMAPTO vector (from transformed to original).
126    omap_data.omap_to.push_back(CreateOmap(Bt.rva, B.rva));
127    omap_data.omap_to.push_back(CreateOmap(Dt.rva, D.rva));
128    omap_data.omap_to.push_back(CreateOmap(Ft.rva, F.rva));
129    omap_data.omap_to.push_back(CreateOmap(Et.rva, E.rva));
130    omap_data.omap_to.push_back(CreateOmap(injected.rva, kInvalidAddress));
131    omap_data.omap_to.push_back(CreateOmap(H1t.rva, H.rva));
132    omap_data.omap_to.push_back(CreateOmap(G1t.rva, G.rva));
133    omap_data.omap_to.push_back(CreateOmap(G2t.rva, G.rva));
134    omap_data.omap_to.push_back(CreateOmap(H2t.rva, H.rva + H1t.length));
135    omap_data.omap_to.push_back(CreateOmap(H2t.end(), kInvalidAddress));
136
137    // Build the OMAPFROM vector (from original to transformed).
138    omap_data.omap_from.push_back(CreateOmap(B.rva, Bt.rva));
139    omap_data.omap_from.push_back(CreateOmap(C.rva, kInvalidAddress));
140    omap_data.omap_from.push_back(CreateOmap(D.rva, Dt.rva));
141    omap_data.omap_from.push_back(CreateOmap(E.rva, Et.rva));
142    omap_data.omap_from.push_back(CreateOmap(F.rva, Ft.rva));
143    omap_data.omap_from.push_back(CreateOmap(G.rva, G1t.rva));
144    omap_data.omap_from.push_back(CreateOmap(H.rva, H1t.rva));
145    omap_data.omap_from.push_back(CreateOmap(H.rva + H1t.length, H2t.rva));
146    omap_data.omap_from.push_back(CreateOmap(H.end(), kInvalidAddress));
147  }
148
149  OmapData omap_data;
150};
151
152}  // namespace
153
154TEST_F(BuildImageMapTest, EmptyImageMapOnEmptyOmapData) {
155  ASSERT_EQ(0u, omap_data.omap_from.size());
156  ASSERT_EQ(0u, omap_data.omap_to.size());
157  ASSERT_EQ(0u, omap_data.length_original);
158
159  ImageMap image_map;
160  BuildImageMap(omap_data, &image_map);
161  EXPECT_EQ(0u, image_map.mapping.size());
162  EXPECT_EQ(0u, image_map.endpoint_index_map.size());
163}
164
165TEST_F(BuildImageMapTest, ImageMapIsCorrect) {
166  InitOmapData();
167  ASSERT_LE(0u, omap_data.omap_from.size());
168  ASSERT_LE(0u, omap_data.omap_to.size());
169  ASSERT_LE(0u, omap_data.length_original);
170
171  ImageMap image_map;
172  BuildImageMap(omap_data, &image_map);
173  EXPECT_LE(9u, image_map.mapping.size());
174  EXPECT_LE(9u, image_map.endpoint_index_map.size());
175
176  Mapping mapping;
177  mapping.push_back(CreateMappedRange(0, 0, B.rva, 0, 0));
178  // C is removed, and it originally comes immediately after B.
179  mapping.push_back(CreateMappedRange(B.rva, Bt.rva, B.length, 0, C.length));
180  // D is shortened by a length of 5.
181  mapping.push_back(CreateMappedRange(D.rva, Dt.rva, Dt.length, 0, 5));
182  // The injected content comes immediately after E in the transformed image.
183  mapping.push_back(CreateMappedRange(E.rva, Et.rva, E.length, injected.length,
184                                      0));
185  mapping.push_back(CreateMappedRange(F.rva, Ft.rva, F.length, 0, 0));
186  // G is copied so creates two entries.
187  mapping.push_back(CreateMappedRange(G.rva, G1t.rva, G.length, 0, 0));
188  mapping.push_back(CreateMappedRange(G.rva, G2t.rva, G.length, 0, 0));
189  // H is split, so create two entries.
190  mapping.push_back(CreateMappedRange(H.rva, H1t.rva, H1t.length, 0, 0));
191  mapping.push_back(CreateMappedRange(H.rva + H1t.length, H2t.rva, H2t.length,
192                                      0, 0));
193  EXPECT_THAT(mapping,
194              testing::ContainerEq(image_map.mapping));
195
196  EndpointIndexMap endpoint_index_map;
197  endpoint_index_map.push_back(CreateEndpointIndex(0, 0));
198  endpoint_index_map.push_back(CreateEndpointIndex(B.rva, 1));
199  endpoint_index_map.push_back(CreateEndpointIndex(D.rva, 2));
200  endpoint_index_map.push_back(CreateEndpointIndex(E.rva, 3));
201  endpoint_index_map.push_back(CreateEndpointIndex(F.rva, 4));
202  // G is duplicated so 2 ranges map back to it, hence the skip from 5 to 7.
203  endpoint_index_map.push_back(CreateEndpointIndex(G.rva, 5));
204  // H is split so we expect 2 endpoints to show up attributed to it.
205  endpoint_index_map.push_back(CreateEndpointIndex(H.rva, 7));
206  endpoint_index_map.push_back(CreateEndpointIndex(H.rva + H1t.length, 8));
207  endpoint_index_map.push_back(CreateEndpointIndex(H.end(), 9));
208  EXPECT_THAT(endpoint_index_map,
209              testing::ContainerEq(image_map.endpoint_index_map));
210}
211
212namespace {
213
214class MapAddressRangeTest : public BuildImageMapTest {
215 public:
216  typedef BuildImageMapTest Super;
217  virtual void SetUp() {
218    Super::SetUp();
219    InitOmapData();
220    BuildImageMap(omap_data, &image_map);
221  }
222
223  ImageMap image_map;
224
225 private:
226  using BuildImageMapTest::InitOmapData;
227  using BuildImageMapTest::omap_data;
228};
229
230}  // namespace
231
232TEST_F(MapAddressRangeTest, EmptyImageMapReturnsIdentity) {
233  ImageMap im;
234  AddressRangeVector mapped_ranges;
235  AddressRange ar(0, 1024);
236  MapAddressRange(im, ar, &mapped_ranges);
237  EXPECT_EQ(1u, mapped_ranges.size());
238  EXPECT_EQ(ar, mapped_ranges[0]);
239}
240
241TEST_F(MapAddressRangeTest, MapOutOfImage) {
242  AddressRangeVector mapped_ranges;
243  MapAddressRange(image_map, AddressRange(H.end() + 10, 10), &mapped_ranges);
244  EXPECT_EQ(0u, mapped_ranges.size());
245}
246
247TEST_F(MapAddressRangeTest, MapIdentity) {
248  AddressRangeVector mapped_ranges;
249  MapAddressRange(image_map, B, &mapped_ranges);
250  EXPECT_EQ(1u, mapped_ranges.size());
251  EXPECT_THAT(mapped_ranges, testing::ElementsAre(B));
252}
253
254TEST_F(MapAddressRangeTest, MapReorderedContiguous) {
255  AddressRangeVector mapped_ranges;
256
257  AddressRange DEF(D.rva, F.end() - D.rva);
258  MapAddressRange(image_map, DEF, &mapped_ranges);
259  EXPECT_EQ(1u, mapped_ranges.size());
260
261  AddressRange DFEt(Dt.rva, Et.end() - Dt.rva);
262  EXPECT_THAT(mapped_ranges, testing::ElementsAre(DFEt));
263}
264
265TEST_F(MapAddressRangeTest, MapEmptySingle) {
266  AddressRangeVector mapped_ranges;
267  MapAddressRange(image_map, AddressRange(D.rva, 0), &mapped_ranges);
268  EXPECT_EQ(1u, mapped_ranges.size());
269  EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(Dt.rva, 0)));
270}
271
272TEST_F(MapAddressRangeTest, MapEmptyCopied) {
273  AddressRangeVector mapped_ranges;
274  MapAddressRange(image_map, AddressRange(G.rva, 0), &mapped_ranges);
275  EXPECT_EQ(2u, mapped_ranges.size());
276  EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(G1t.rva, 0),
277                                                  AddressRange(G2t.rva, 0)));
278}
279
280TEST_F(MapAddressRangeTest, MapCopiedContiguous) {
281  AddressRangeVector mapped_ranges;
282  MapAddressRange(image_map, G, &mapped_ranges);
283  EXPECT_EQ(1u, mapped_ranges.size());
284  EXPECT_THAT(mapped_ranges, testing::ElementsAre(
285      AddressRange(G1t.rva, G2t.end() - G1t.rva)));
286}
287
288TEST_F(MapAddressRangeTest, MapSplitDiscontiguous) {
289  AddressRangeVector mapped_ranges;
290  MapAddressRange(image_map, H, &mapped_ranges);
291  EXPECT_EQ(2u, mapped_ranges.size());
292  EXPECT_THAT(mapped_ranges, testing::ElementsAre(H1t, H2t));
293}
294
295TEST_F(MapAddressRangeTest, MapInjected) {
296  AddressRangeVector mapped_ranges;
297
298  AddressRange EFGH(E.rva, H.end() - E.rva);
299  MapAddressRange(image_map, EFGH, &mapped_ranges);
300  EXPECT_EQ(1u, mapped_ranges.size());
301
302  AddressRange FEHGGHt(Ft.rva, H2t.end() - Ft.rva);
303  EXPECT_THAT(mapped_ranges, testing::ElementsAre(FEHGGHt));
304}
305
306TEST_F(MapAddressRangeTest, MapRemovedEntirely) {
307  AddressRangeVector mapped_ranges;
308  MapAddressRange(image_map, C, &mapped_ranges);
309  EXPECT_EQ(0u, mapped_ranges.size());
310}
311
312TEST_F(MapAddressRangeTest, MapRemovedPartly) {
313  AddressRangeVector mapped_ranges;
314  MapAddressRange(image_map, D, &mapped_ranges);
315  EXPECT_EQ(1u, mapped_ranges.size());
316  EXPECT_THAT(mapped_ranges, testing::ElementsAre(Dt));
317}
318
319TEST_F(MapAddressRangeTest, MapFull) {
320  AddressRangeVector mapped_ranges;
321
322  AddressRange AH(0, H.end());
323  MapAddressRange(image_map, AH, &mapped_ranges);
324  EXPECT_EQ(1u, mapped_ranges.size());
325
326  AddressRange AHt(0, H2t.end());
327  EXPECT_THAT(mapped_ranges, testing::ElementsAre(AHt));
328}
329
330}  // namespace google_breakpad
331