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