/ src / common / linux / file_id_unittest.cc
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  }