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