/ src / common / windows / omap_unittest.cc
omap_unittest.cc
  1  // Copyright 2013 Google LLC
  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 LLC 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  #ifdef HAVE_CONFIG_H
 32  #include <config.h>  // Must come first
 33  #endif
 34  
 35  #include "common/windows/omap.h"
 36  
 37  #include "breakpad_googletest_includes.h"
 38  
 39  namespace google_breakpad {
 40  
 41  // Equality operators for ContainerEq. These must be outside of the anonymous
 42  // namespace in order for them to be found.
 43  bool operator==(const MappedRange& mr1, const MappedRange& mr2) {
 44    return mr1.rva_original == mr2.rva_original &&
 45        mr1.rva_transformed == mr2.rva_transformed &&
 46        mr1.length == mr2.length &&
 47        mr1.injected == mr2.injected &&
 48        mr1.removed == mr2.removed;
 49  }
 50  bool operator==(const EndpointIndex& ei1, const EndpointIndex& ei2) {
 51    return ei1.endpoint == ei2.endpoint && ei1.index == ei2.index;
 52  }
 53  
 54  // Pretty printers for more meaningful error messages. Also need to be outside
 55  // the anonymous namespace.
 56  std::ostream& operator<<(std::ostream& os, const MappedRange& mr) {
 57    os << "MappedRange(rva_original=" << mr.rva_original
 58       << ", rva_transformed=" << mr.rva_transformed
 59       << ", length=" << mr.length
 60       << ", injected=" << mr.injected
 61       << ", removed=" << mr.removed << ")";
 62    return os;
 63  }
 64  std::ostream& operator<<(std::ostream& os, const EndpointIndex& ei) {
 65    os << "EndpointIndex(endpoint=" << ei.endpoint
 66       << ", index=" << ei.index << ")";
 67    return os;
 68  }
 69  std::ostream& operator<<(std::ostream& os, const AddressRange& ar) {
 70    os << "AddressRange(rva=" << ar.rva << ", length=" << ar.length << ")";
 71    return os;
 72  }
 73  
 74  namespace {
 75  
 76  OMAP CreateOmap(DWORD rva, DWORD rvaTo) {
 77    OMAP o = { rva, rvaTo };
 78    return o;
 79  }
 80  
 81  MappedRange CreateMappedRange(DWORD rva_original,
 82                                DWORD rva_transformed,
 83                                DWORD length,
 84                                DWORD injected,
 85                                DWORD removed) {
 86    MappedRange mr = { rva_original, rva_transformed, length, injected, removed };
 87    return mr;
 88  }
 89  
 90  EndpointIndex CreateEndpointIndex(DWORD endpoint, size_t index) {
 91    EndpointIndex ei = { endpoint, index };
 92    return ei;
 93  }
 94  
 95  //              (C is removed)
 96  // Original   :  A B C D E F G H
 97  // Transformed:  A B D F E * H1 G1 G2 H2
 98  //              (* is injected, G is copied, H is split)
 99  // A is implied.
100  
101  // Layout of the original image.
102  const AddressRange B(100, 15);
103  const AddressRange C(B.end(), 10);
104  const AddressRange D(C.end(), 25);
105  const AddressRange E(D.end(), 10);
106  const AddressRange F(E.end(), 40);
107  const AddressRange G(F.end(), 3);
108  const AddressRange H(G.end(), 7);
109  
110  // Layout of the transformed image.
111  const AddressRange Bt(100, 15);
112  const AddressRange Dt(Bt.end(), 20);  // D is shortened.
113  const AddressRange Ft(Dt.end(), F.length);
114  const AddressRange Et(Ft.end(), E.length);
115  const AddressRange injected(Et.end(), 5);
116  const AddressRange H1t(injected.end(), 4);  // H is split.
117  const AddressRange G1t(H1t.end(), G.length);  // G is copied.
118  const AddressRange G2t(G1t.end(), G.length);  // G is copied.
119  const AddressRange H2t(G2t.end(), 3);  // H is split.
120  
121  class BuildImageMapTest : public testing::Test {
122   public:
123    static const DWORD kInvalidAddress = 0xFFFFFFFF;
124  
125    void InitOmapData() {
126      omap_data.length_original = H.end();
127  
128      // Build the OMAPTO vector (from transformed to original).
129      omap_data.omap_to.push_back(CreateOmap(Bt.rva, B.rva));
130      omap_data.omap_to.push_back(CreateOmap(Dt.rva, D.rva));
131      omap_data.omap_to.push_back(CreateOmap(Ft.rva, F.rva));
132      omap_data.omap_to.push_back(CreateOmap(Et.rva, E.rva));
133      omap_data.omap_to.push_back(CreateOmap(injected.rva, kInvalidAddress));
134      omap_data.omap_to.push_back(CreateOmap(H1t.rva, H.rva));
135      omap_data.omap_to.push_back(CreateOmap(G1t.rva, G.rva));
136      omap_data.omap_to.push_back(CreateOmap(G2t.rva, G.rva));
137      omap_data.omap_to.push_back(CreateOmap(H2t.rva, H.rva + H1t.length));
138      omap_data.omap_to.push_back(CreateOmap(H2t.end(), kInvalidAddress));
139  
140      // Build the OMAPFROM vector (from original to transformed).
141      omap_data.omap_from.push_back(CreateOmap(B.rva, Bt.rva));
142      omap_data.omap_from.push_back(CreateOmap(C.rva, kInvalidAddress));
143      omap_data.omap_from.push_back(CreateOmap(D.rva, Dt.rva));
144      omap_data.omap_from.push_back(CreateOmap(E.rva, Et.rva));
145      omap_data.omap_from.push_back(CreateOmap(F.rva, Ft.rva));
146      omap_data.omap_from.push_back(CreateOmap(G.rva, G1t.rva));
147      omap_data.omap_from.push_back(CreateOmap(H.rva, H1t.rva));
148      omap_data.omap_from.push_back(CreateOmap(H.rva + H1t.length, H2t.rva));
149      omap_data.omap_from.push_back(CreateOmap(H.end(), kInvalidAddress));
150    }
151  
152    OmapData omap_data;
153  };
154  
155  }  // namespace
156  
157  TEST_F(BuildImageMapTest, EmptyImageMapOnEmptyOmapData) {
158    ASSERT_EQ(0u, omap_data.omap_from.size());
159    ASSERT_EQ(0u, omap_data.omap_to.size());
160    ASSERT_EQ(0u, omap_data.length_original);
161  
162    ImageMap image_map;
163    BuildImageMap(omap_data, &image_map);
164    EXPECT_EQ(0u, image_map.mapping.size());
165    EXPECT_EQ(0u, image_map.endpoint_index_map.size());
166  }
167  
168  TEST_F(BuildImageMapTest, ImageMapIsCorrect) {
169    InitOmapData();
170    ASSERT_LE(0u, omap_data.omap_from.size());
171    ASSERT_LE(0u, omap_data.omap_to.size());
172    ASSERT_LE(0u, omap_data.length_original);
173  
174    ImageMap image_map;
175    BuildImageMap(omap_data, &image_map);
176    EXPECT_LE(9u, image_map.mapping.size());
177    EXPECT_LE(9u, image_map.endpoint_index_map.size());
178  
179    Mapping mapping;
180    mapping.push_back(CreateMappedRange(0, 0, B.rva, 0, 0));
181    // C is removed, and it originally comes immediately after B.
182    mapping.push_back(CreateMappedRange(B.rva, Bt.rva, B.length, 0, C.length));
183    // D is shortened by a length of 5.
184    mapping.push_back(CreateMappedRange(D.rva, Dt.rva, Dt.length, 0, 5));
185    // The injected content comes immediately after E in the transformed image.
186    mapping.push_back(CreateMappedRange(E.rva, Et.rva, E.length, injected.length,
187                                        0));
188    mapping.push_back(CreateMappedRange(F.rva, Ft.rva, F.length, 0, 0));
189    // G is copied so creates two entries.
190    mapping.push_back(CreateMappedRange(G.rva, G1t.rva, G.length, 0, 0));
191    mapping.push_back(CreateMappedRange(G.rva, G2t.rva, G.length, 0, 0));
192    // H is split, so create two entries.
193    mapping.push_back(CreateMappedRange(H.rva, H1t.rva, H1t.length, 0, 0));
194    mapping.push_back(CreateMappedRange(H.rva + H1t.length, H2t.rva, H2t.length,
195                                        0, 0));
196    EXPECT_THAT(mapping,
197                testing::ContainerEq(image_map.mapping));
198  
199    EndpointIndexMap endpoint_index_map;
200    endpoint_index_map.push_back(CreateEndpointIndex(0, 0));
201    endpoint_index_map.push_back(CreateEndpointIndex(B.rva, 1));
202    endpoint_index_map.push_back(CreateEndpointIndex(D.rva, 2));
203    endpoint_index_map.push_back(CreateEndpointIndex(E.rva, 3));
204    endpoint_index_map.push_back(CreateEndpointIndex(F.rva, 4));
205    // G is duplicated so 2 ranges map back to it, hence the skip from 5 to 7.
206    endpoint_index_map.push_back(CreateEndpointIndex(G.rva, 5));
207    // H is split so we expect 2 endpoints to show up attributed to it.
208    endpoint_index_map.push_back(CreateEndpointIndex(H.rva, 7));
209    endpoint_index_map.push_back(CreateEndpointIndex(H.rva + H1t.length, 8));
210    endpoint_index_map.push_back(CreateEndpointIndex(H.end(), 9));
211    EXPECT_THAT(endpoint_index_map,
212                testing::ContainerEq(image_map.endpoint_index_map));
213  }
214  
215  namespace {
216  
217  class MapAddressRangeTest : public BuildImageMapTest {
218   public:
219    typedef BuildImageMapTest Super;
220    virtual void SetUp() {
221      Super::SetUp();
222      InitOmapData();
223      BuildImageMap(omap_data, &image_map);
224    }
225  
226    ImageMap image_map;
227  
228   private:
229    using BuildImageMapTest::InitOmapData;
230    using BuildImageMapTest::omap_data;
231  };
232  
233  }  // namespace
234  
235  TEST_F(MapAddressRangeTest, EmptyImageMapReturnsIdentity) {
236    ImageMap im;
237    AddressRangeVector mapped_ranges;
238    AddressRange ar(0, 1024);
239    MapAddressRange(im, ar, &mapped_ranges);
240    EXPECT_EQ(1u, mapped_ranges.size());
241    EXPECT_EQ(ar, mapped_ranges[0]);
242  }
243  
244  TEST_F(MapAddressRangeTest, MapOutOfImage) {
245    AddressRangeVector mapped_ranges;
246    MapAddressRange(image_map, AddressRange(H.end() + 10, 10), &mapped_ranges);
247    EXPECT_EQ(0u, mapped_ranges.size());
248  }
249  
250  TEST_F(MapAddressRangeTest, MapIdentity) {
251    AddressRangeVector mapped_ranges;
252    MapAddressRange(image_map, B, &mapped_ranges);
253    EXPECT_EQ(1u, mapped_ranges.size());
254    EXPECT_THAT(mapped_ranges, testing::ElementsAre(B));
255  }
256  
257  TEST_F(MapAddressRangeTest, MapReorderedContiguous) {
258    AddressRangeVector mapped_ranges;
259  
260    AddressRange DEF(D.rva, F.end() - D.rva);
261    MapAddressRange(image_map, DEF, &mapped_ranges);
262    EXPECT_EQ(1u, mapped_ranges.size());
263  
264    AddressRange DFEt(Dt.rva, Et.end() - Dt.rva);
265    EXPECT_THAT(mapped_ranges, testing::ElementsAre(DFEt));
266  }
267  
268  TEST_F(MapAddressRangeTest, MapEmptySingle) {
269    AddressRangeVector mapped_ranges;
270    MapAddressRange(image_map, AddressRange(D.rva, 0), &mapped_ranges);
271    EXPECT_EQ(1u, mapped_ranges.size());
272    EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(Dt.rva, 0)));
273  }
274  
275  TEST_F(MapAddressRangeTest, MapEmptyCopied) {
276    AddressRangeVector mapped_ranges;
277    MapAddressRange(image_map, AddressRange(G.rva, 0), &mapped_ranges);
278    EXPECT_EQ(2u, mapped_ranges.size());
279    EXPECT_THAT(mapped_ranges, testing::ElementsAre(AddressRange(G1t.rva, 0),
280                                                    AddressRange(G2t.rva, 0)));
281  }
282  
283  TEST_F(MapAddressRangeTest, MapCopiedContiguous) {
284    AddressRangeVector mapped_ranges;
285    MapAddressRange(image_map, G, &mapped_ranges);
286    EXPECT_EQ(1u, mapped_ranges.size());
287    EXPECT_THAT(mapped_ranges, testing::ElementsAre(
288        AddressRange(G1t.rva, G2t.end() - G1t.rva)));
289  }
290  
291  TEST_F(MapAddressRangeTest, MapSplitDiscontiguous) {
292    AddressRangeVector mapped_ranges;
293    MapAddressRange(image_map, H, &mapped_ranges);
294    EXPECT_EQ(2u, mapped_ranges.size());
295    EXPECT_THAT(mapped_ranges, testing::ElementsAre(H1t, H2t));
296  }
297  
298  TEST_F(MapAddressRangeTest, MapInjected) {
299    AddressRangeVector mapped_ranges;
300  
301    AddressRange EFGH(E.rva, H.end() - E.rva);
302    MapAddressRange(image_map, EFGH, &mapped_ranges);
303    EXPECT_EQ(1u, mapped_ranges.size());
304  
305    AddressRange FEHGGHt(Ft.rva, H2t.end() - Ft.rva);
306    EXPECT_THAT(mapped_ranges, testing::ElementsAre(FEHGGHt));
307  }
308  
309  TEST_F(MapAddressRangeTest, MapRemovedEntirely) {
310    AddressRangeVector mapped_ranges;
311    MapAddressRange(image_map, C, &mapped_ranges);
312    EXPECT_EQ(0u, mapped_ranges.size());
313  }
314  
315  TEST_F(MapAddressRangeTest, MapRemovedPartly) {
316    AddressRangeVector mapped_ranges;
317    MapAddressRange(image_map, D, &mapped_ranges);
318    EXPECT_EQ(1u, mapped_ranges.size());
319    EXPECT_THAT(mapped_ranges, testing::ElementsAre(Dt));
320  }
321  
322  TEST_F(MapAddressRangeTest, MapFull) {
323    AddressRangeVector mapped_ranges;
324  
325    AddressRange AH(0, H.end());
326    MapAddressRange(image_map, AH, &mapped_ranges);
327    EXPECT_EQ(1u, mapped_ranges.size());
328  
329    AddressRange AHt(0, H2t.end());
330    EXPECT_THAT(mapped_ranges, testing::ElementsAre(AHt));
331  }
332  
333  }  // namespace google_breakpad