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