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 #include <vector>
37 
38 #include "common/linux/elf_gnu_compat.h"
39 #include "common/linux/elfutils.h"
40 #include "common/linux/file_id.h"
41 #include "common/linux/safe_readlink.h"
42 #include "common/linux/synth_elf.h"
43 #include "common/test_assembler.h"
44 #include "common/tests/auto_tempdir.h"
45 #include "common/using_std_string.h"
46 #include "breakpad_googletest_includes.h"
47 
48 using namespace google_breakpad;
49 using google_breakpad::synth_elf::ELF;
50 using google_breakpad::synth_elf::Notes;
51 using google_breakpad::test_assembler::kLittleEndian;
52 using google_breakpad::test_assembler::Section;
53 using std::vector;
54 using ::testing::Types;
55 
56 namespace {
57 
58 // Simply calling Section::Append(size, byte) produces a uninteresting pattern
59 // that tends to get hashed to 0000...0000. This populates the section with
60 // data to produce better hashes.
PopulateSection(Section * section,int size,int prime_number)61 void PopulateSection(Section* section, int size, int prime_number) {
62   for (int i = 0; i < size; i++)
63     section->Append(1, (i % prime_number) % 256);
64 }
65 
66 typedef wasteful_vector<uint8_t> id_vector;
67 
68 }  // namespace
69 
70 #ifndef __ANDROID__
71 // This test is disabled on Android: It will always fail, since there is no
72 // 'strip' binary installed on test devices.
TEST(FileIDStripTest,StripSelf)73 TEST(FileIDStripTest, StripSelf) {
74   // Calculate the File ID of this binary using
75   // FileID::ElfFileIdentifier, then make a copy of this binary,
76   // strip it, and ensure that the result is the same.
77   char exe_name[PATH_MAX];
78   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
79 
80   // copy our binary to a temp file, and strip it
81   AutoTempDir temp_dir;
82   string templ = temp_dir.path() + "/file-id-unittest";
83   char cmdline[4096];
84   sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str());
85   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
86   sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str());
87   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
88   sprintf(cmdline, "strip \"%s\"", templ.c_str());
89   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
90 
91   PageAllocator allocator;
92   id_vector identifier1(&allocator, kDefaultBuildIdSize);
93   id_vector identifier2(&allocator, kDefaultBuildIdSize);
94 
95   FileID fileid1(exe_name);
96   EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1));
97   FileID fileid2(templ.c_str());
98   EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2));
99 
100   string identifier_string1 =
101       FileID::ConvertIdentifierToUUIDString(identifier1);
102   string identifier_string2 =
103       FileID::ConvertIdentifierToUUIDString(identifier2);
104   EXPECT_EQ(identifier_string1, identifier_string2);
105 }
106 #endif  // !__ANDROID__
107 
108 template<typename ElfClass>
109 class FileIDTest : public testing::Test {
110 public:
GetElfContents(ELF & elf)111   void GetElfContents(ELF& elf) {
112     string contents;
113     ASSERT_TRUE(elf.GetContents(&contents));
114     ASSERT_LT(0U, contents.size());
115 
116     elfdata_v.clear();
117     elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
118     elfdata = &elfdata_v[0];
119   }
120 
make_vector()121   id_vector make_vector() {
122     return id_vector(&allocator, kDefaultBuildIdSize);
123   }
124 
125   template<size_t N>
get_file_id(const uint8_t (& data)[N])126   string get_file_id(const uint8_t (&data)[N]) {
127     id_vector expected_identifier(make_vector());
128     expected_identifier.insert(expected_identifier.end(),
129                                &data[0],
130                                data + N);
131     return FileID::ConvertIdentifierToUUIDString(expected_identifier);
132   }
133 
134   vector<uint8_t> elfdata_v;
135   uint8_t* elfdata;
136   PageAllocator allocator;
137 };
138 
139 typedef Types<ElfClass32, ElfClass64> ElfClasses;
140 
141 TYPED_TEST_SUITE(FileIDTest, ElfClasses);
142 
TYPED_TEST(FileIDTest,ElfClass)143 TYPED_TEST(FileIDTest, ElfClass) {
144   const char expected_identifier_string[] =
145       "80808080808000000000008080808080";
146   const size_t kTextSectionSize = 128;
147 
148   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
149   Section text(kLittleEndian);
150   for (size_t i = 0; i < kTextSectionSize; ++i) {
151     text.D8(i * 3);
152   }
153   elf.AddSection(".text", text, SHT_PROGBITS);
154   elf.Finish();
155   this->GetElfContents(elf);
156 
157   id_vector identifier(this->make_vector());
158   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
159                                                       identifier));
160 
161   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
162   EXPECT_EQ(expected_identifier_string, identifier_string);
163 }
164 
TYPED_TEST(FileIDTest,BuildID)165 TYPED_TEST(FileIDTest, BuildID) {
166   const uint8_t kExpectedIdentifierBytes[] =
167     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
168      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
169      0x10, 0x11, 0x12, 0x13};
170   const string expected_identifier_string =
171       this->get_file_id(kExpectedIdentifierBytes);
172 
173   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
174   Section text(kLittleEndian);
175   text.Append(4096, 0);
176   elf.AddSection(".text", text, SHT_PROGBITS);
177   Notes notes(kLittleEndian);
178   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
179                 sizeof(kExpectedIdentifierBytes));
180   elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
181   elf.Finish();
182   this->GetElfContents(elf);
183 
184   id_vector identifier(this->make_vector());
185   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
186                                                       identifier));
187   EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
188 
189   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
190   EXPECT_EQ(expected_identifier_string, identifier_string);
191 }
192 
193 // Test that a build id note with fewer bytes than usual is handled.
TYPED_TEST(FileIDTest,BuildIDShort)194 TYPED_TEST(FileIDTest, BuildIDShort) {
195   const uint8_t kExpectedIdentifierBytes[] =
196     {0x00, 0x01, 0x02, 0x03};
197   const string expected_identifier_string =
198       this->get_file_id(kExpectedIdentifierBytes);
199 
200   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
201   Section text(kLittleEndian);
202   text.Append(4096, 0);
203   elf.AddSection(".text", text, SHT_PROGBITS);
204   Notes notes(kLittleEndian);
205   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
206                 sizeof(kExpectedIdentifierBytes));
207   elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
208   elf.Finish();
209   this->GetElfContents(elf);
210 
211   id_vector identifier(this->make_vector());
212   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
213                                                       identifier));
214   EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
215 
216   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
217   EXPECT_EQ(expected_identifier_string, identifier_string);
218 }
219 
220 // Test that a build id note with more bytes than usual is handled.
TYPED_TEST(FileIDTest,BuildIDLong)221 TYPED_TEST(FileIDTest, BuildIDLong) {
222   const uint8_t kExpectedIdentifierBytes[] =
223     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
224      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
225      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
226      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
227   const string expected_identifier_string =
228       this->get_file_id(kExpectedIdentifierBytes);
229 
230   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
231   Section text(kLittleEndian);
232   text.Append(4096, 0);
233   elf.AddSection(".text", text, SHT_PROGBITS);
234   Notes notes(kLittleEndian);
235   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
236                 sizeof(kExpectedIdentifierBytes));
237   elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
238   elf.Finish();
239   this->GetElfContents(elf);
240 
241   id_vector identifier(this->make_vector());
242   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
243                                                       identifier));
244   EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
245 
246   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
247   EXPECT_EQ(expected_identifier_string, identifier_string);
248 }
249 
TYPED_TEST(FileIDTest,BuildIDPH)250 TYPED_TEST(FileIDTest, BuildIDPH) {
251   const uint8_t kExpectedIdentifierBytes[] =
252     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
253      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
254      0x10, 0x11, 0x12, 0x13};
255   const string expected_identifier_string =
256       this->get_file_id(kExpectedIdentifierBytes);
257 
258   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
259   Section text(kLittleEndian);
260   text.Append(4096, 0);
261   elf.AddSection(".text", text, SHT_PROGBITS);
262   Notes notes(kLittleEndian);
263   notes.AddNote(0, "Linux",
264                 reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
265   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
266                 sizeof(kExpectedIdentifierBytes));
267   int note_idx = elf.AddSection(".note", notes, SHT_NOTE);
268   elf.AddSegment(note_idx, note_idx, PT_NOTE);
269   elf.Finish();
270   this->GetElfContents(elf);
271 
272   id_vector identifier(this->make_vector());
273   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
274                                                       identifier));
275   EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
276 
277   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
278   EXPECT_EQ(expected_identifier_string, identifier_string);
279 }
280 
TYPED_TEST(FileIDTest,BuildIDMultiplePH)281 TYPED_TEST(FileIDTest, BuildIDMultiplePH) {
282   const uint8_t kExpectedIdentifierBytes[] =
283     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
284      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
285      0x10, 0x11, 0x12, 0x13};
286   const string expected_identifier_string =
287       this->get_file_id(kExpectedIdentifierBytes);
288 
289   ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
290   Section text(kLittleEndian);
291   text.Append(4096, 0);
292   elf.AddSection(".text", text, SHT_PROGBITS);
293   Notes notes1(kLittleEndian);
294   notes1.AddNote(0, "Linux",
295                 reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
296   Notes notes2(kLittleEndian);
297   notes2.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
298                  sizeof(kExpectedIdentifierBytes));
299   int note1_idx = elf.AddSection(".note1", notes1, SHT_NOTE);
300   int note2_idx = elf.AddSection(".note2", notes2, SHT_NOTE);
301   elf.AddSegment(note1_idx, note1_idx, PT_NOTE);
302   elf.AddSegment(note2_idx, note2_idx, PT_NOTE);
303   elf.Finish();
304   this->GetElfContents(elf);
305 
306   id_vector identifier(this->make_vector());
307   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
308                                                       identifier));
309   EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
310 
311   string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
312   EXPECT_EQ(expected_identifier_string, identifier_string);
313 }
314 
315 // Test to make sure two files with different text sections produce
316 // different hashes when not using a build id.
TYPED_TEST(FileIDTest,UniqueHashes)317 TYPED_TEST(FileIDTest, UniqueHashes) {
318   {
319     ELF elf1(EM_386, TypeParam::kClass, kLittleEndian);
320     Section foo_1(kLittleEndian);
321     PopulateSection(&foo_1, 32, 5);
322     elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
323     Section text_1(kLittleEndian);
324     PopulateSection(&text_1, 4096, 17);
325     elf1.AddSection(".text", text_1, SHT_PROGBITS);
326     elf1.Finish();
327     this->GetElfContents(elf1);
328   }
329 
330   id_vector identifier_1(this->make_vector());
331   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
332                                                       identifier_1));
333   string identifier_string_1 =
334       FileID::ConvertIdentifierToUUIDString(identifier_1);
335 
336   {
337     ELF elf2(EM_386, TypeParam::kClass, kLittleEndian);
338     Section text_2(kLittleEndian);
339     Section foo_2(kLittleEndian);
340     PopulateSection(&foo_2, 32, 5);
341     elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
342     PopulateSection(&text_2, 4096, 31);
343     elf2.AddSection(".text", text_2, SHT_PROGBITS);
344     elf2.Finish();
345     this->GetElfContents(elf2);
346   }
347 
348   id_vector identifier_2(this->make_vector());
349   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
350                                                       identifier_2));
351   string identifier_string_2 =
352       FileID::ConvertIdentifierToUUIDString(identifier_2);
353 
354   EXPECT_NE(identifier_string_1, identifier_string_2);
355 }
356 
TYPED_TEST(FileIDTest,ConvertIdentifierToString)357 TYPED_TEST(FileIDTest, ConvertIdentifierToString) {
358   const uint8_t kIdentifierBytes[] =
359     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
360      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
361      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
362      0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
363   const char* kExpected =
364     "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
365 
366   id_vector identifier(this->make_vector());
367   identifier.insert(identifier.end(),
368                     kIdentifierBytes,
369                     kIdentifierBytes + sizeof(kIdentifierBytes));
370   ASSERT_EQ(kExpected,
371             FileID::ConvertIdentifierToString(identifier));
372 }
373