file_id_unittest.cc
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. 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. 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: 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 135 id_vector make_vector() { 136 return id_vector(&allocator, kDefaultBuildIdSize); 137 } 138 139 template<size_t 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 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 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. 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. 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 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 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. 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 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 }