1// Copyright (c) 2010, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// Unit tests for FileID
31
32#include <elf.h>
33#include <stdlib.h>
34
35#include <string>
36
37#include "common/linux/elf_gnu_compat.h"
38#include "common/linux/elfutils.h"
39#include "common/linux/file_id.h"
40#include "common/linux/safe_readlink.h"
41#include "common/linux/synth_elf.h"
42#include "common/test_assembler.h"
43#include "common/tests/auto_tempdir.h"
44#include "common/using_std_string.h"
45#include "breakpad_googletest_includes.h"
46
47using namespace google_breakpad;
48using google_breakpad::ElfClass32;
49using google_breakpad::ElfClass64;
50using google_breakpad::SafeReadLink;
51using google_breakpad::synth_elf::ELF;
52using google_breakpad::synth_elf::Notes;
53using google_breakpad::test_assembler::kLittleEndian;
54using google_breakpad::test_assembler::Section;
55using ::testing::Types;
56
57namespace {
58
59// Simply calling Section::Append(size, byte) produces a uninteresting pattern
60// that tends to get hashed to 0000...0000. This populates the section with
61// data to produce better hashes.
62void PopulateSection(Section* section, int size, int prime_number) {
63  for (int i = 0; i < size; i++)
64    section->Append(1, (i % prime_number) % 256);
65}
66
67}  // namespace
68
69#ifndef __ANDROID__
70// This test is disabled on Android: It will always fail, since there is no
71// 'strip' binary installed on test devices.
72TEST(FileIDStripTest, StripSelf) {
73  // Calculate the File ID of this binary using
74  // FileID::ElfFileIdentifier, then make a copy of this binary,
75  // strip it, and ensure that the result is the same.
76  char exe_name[PATH_MAX];
77  ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
78
79  // copy our binary to a temp file, and strip it
80  AutoTempDir temp_dir;
81  string templ = temp_dir.path() + "/file-id-unittest";
82  char cmdline[4096];
83  sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str());
84  ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
85  sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str());
86  ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
87  sprintf(cmdline, "strip \"%s\"", templ.c_str());
88  ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
89
90  uint8_t identifier1[sizeof(MDGUID)];
91  uint8_t identifier2[sizeof(MDGUID)];
92  FileID fileid1(exe_name);
93  EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1));
94  FileID fileid2(templ.c_str());
95  EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2));
96  char identifier_string1[37];
97  char identifier_string2[37];
98  FileID::ConvertIdentifierToString(identifier1, identifier_string1,
99                                    37);
100  FileID::ConvertIdentifierToString(identifier2, identifier_string2,
101                                    37);
102  EXPECT_STREQ(identifier_string1, identifier_string2);
103}
104#endif  // !__ANDROID__
105
106template<typename ElfClass>
107class FileIDTest : public testing::Test {
108public:
109  void GetElfContents(ELF& elf) {
110    string contents;
111    ASSERT_TRUE(elf.GetContents(&contents));
112    ASSERT_LT(0U, contents.size());
113
114    elfdata_v.clear();
115    elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
116    elfdata = &elfdata_v[0];
117  }
118
119  vector<uint8_t> elfdata_v;
120  uint8_t* elfdata;
121};
122
123typedef Types<ElfClass32, ElfClass64> ElfClasses;
124
125TYPED_TEST_CASE(FileIDTest, ElfClasses);
126
127TYPED_TEST(FileIDTest, ElfClass) {
128  uint8_t identifier[sizeof(MDGUID)];
129  const char expected_identifier_string[] =
130      "80808080-8080-0000-0000-008080808080";
131  char identifier_string[sizeof(expected_identifier_string)];
132  const size_t kTextSectionSize = 128;
133
134  ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
135  Section text(kLittleEndian);
136  for (size_t i = 0; i < kTextSectionSize; ++i) {
137    text.D8(i * 3);
138  }
139  elf.AddSection(".text", text, SHT_PROGBITS);
140  elf.Finish();
141  this->GetElfContents(elf);
142
143  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
144                                                      identifier));
145
146  FileID::ConvertIdentifierToString(identifier, identifier_string,
147                                    sizeof(identifier_string));
148  EXPECT_STREQ(expected_identifier_string, identifier_string);
149}
150
151TYPED_TEST(FileIDTest, BuildID) {
152  const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
153    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
154     0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
155  char expected_identifier_string[] =
156    "00000000-0000-0000-0000-000000000000";
157  FileID::ConvertIdentifierToString(kExpectedIdentifier,
158                                    expected_identifier_string,
159                                    sizeof(expected_identifier_string));
160
161  uint8_t identifier[sizeof(MDGUID)];
162  char identifier_string[sizeof(expected_identifier_string)];
163
164  ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
165  Section text(kLittleEndian);
166  text.Append(4096, 0);
167  elf.AddSection(".text", text, SHT_PROGBITS);
168  Notes notes(kLittleEndian);
169  notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
170                sizeof(kExpectedIdentifier));
171  elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
172  elf.Finish();
173  this->GetElfContents(elf);
174
175  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
176                                                      identifier));
177
178  FileID::ConvertIdentifierToString(identifier, identifier_string,
179                                    sizeof(identifier_string));
180  EXPECT_STREQ(expected_identifier_string, identifier_string);
181}
182
183TYPED_TEST(FileIDTest, BuildIDPH) {
184  const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
185    {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
186     0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
187  char expected_identifier_string[] =
188    "00000000-0000-0000-0000-000000000000";
189  FileID::ConvertIdentifierToString(kExpectedIdentifier,
190                                    expected_identifier_string,
191                                    sizeof(expected_identifier_string));
192
193  uint8_t identifier[sizeof(MDGUID)];
194  char identifier_string[sizeof(expected_identifier_string)];
195
196  ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
197  Section text(kLittleEndian);
198  text.Append(4096, 0);
199  elf.AddSection(".text", text, SHT_PROGBITS);
200  Notes notes(kLittleEndian);
201  notes.AddNote(0, "Linux",
202                reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
203  notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
204                sizeof(kExpectedIdentifier));
205  int note_idx = elf.AddSection(".note", notes, SHT_NOTE);
206  elf.AddSegment(note_idx, note_idx, PT_NOTE);
207  elf.Finish();
208  this->GetElfContents(elf);
209
210  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
211                                                      identifier));
212
213  FileID::ConvertIdentifierToString(identifier, identifier_string,
214                                    sizeof(identifier_string));
215  EXPECT_STREQ(expected_identifier_string, identifier_string);
216}
217
218// Test to make sure two files with different text sections produce
219// different hashes when not using a build id.
220TYPED_TEST(FileIDTest, UniqueHashes) {
221  char identifier_string_1[] =
222    "00000000-0000-0000-0000-000000000000";
223  char identifier_string_2[] =
224    "00000000-0000-0000-0000-000000000000";
225  uint8_t identifier_1[sizeof(MDGUID)];
226  uint8_t identifier_2[sizeof(MDGUID)];
227
228  {
229    ELF elf1(EM_386, TypeParam::kClass, kLittleEndian);
230    Section foo_1(kLittleEndian);
231    PopulateSection(&foo_1, 32, 5);
232    elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
233    Section text_1(kLittleEndian);
234    PopulateSection(&text_1, 4096, 17);
235    elf1.AddSection(".text", text_1, SHT_PROGBITS);
236    elf1.Finish();
237    this->GetElfContents(elf1);
238  }
239
240  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
241                                                      identifier_1));
242  FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
243                                    sizeof(identifier_string_1));
244
245  {
246    ELF elf2(EM_386, TypeParam::kClass, kLittleEndian);
247    Section text_2(kLittleEndian);
248    Section foo_2(kLittleEndian);
249    PopulateSection(&foo_2, 32, 5);
250    elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
251    PopulateSection(&text_2, 4096, 31);
252    elf2.AddSection(".text", text_2, SHT_PROGBITS);
253    elf2.Finish();
254    this->GetElfContents(elf2);
255  }
256
257  EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
258                                                      identifier_2));
259  FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
260                                    sizeof(identifier_string_2));
261
262  EXPECT_STRNE(identifier_string_1, identifier_string_2);
263}
264