1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <unistd.h> 6 7 #include <vector> 8 9 #include "gtest/gtest.h" 10 11 #include "puffin/src/file_stream.h" 12 #include "puffin/src/include/puffin/common.h" 13 #include "puffin/src/include/puffin/utils.h" 14 #include "puffin/src/memory_stream.h" 15 #include "puffin/src/unittest_common.h" 16 17 namespace puffin { 18 19 using std::string; 20 using std::vector; 21 22 namespace { 23 const uint8_t kZipEntries[] = { 24 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0xfc, 0x88, 25 0x28, 0x4c, 0xcb, 0x86, 0xe1, 0x80, 0x06, 0x00, 0x00, 0x00, 0x09, 0x00, 26 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x31, 0x55, 0x54, 0x09, 0x00, 0x03, 27 0xec, 0x15, 0x54, 0x5a, 0x49, 0x10, 0x54, 0x5a, 0x75, 0x78, 0x0b, 0x00, 28 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 29 0x34, 0x84, 0x00, 0x2e, 0x00, 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 30 0x00, 0x08, 0x00, 0x01, 0x89, 0x28, 0x4c, 0xe0, 0xe8, 0x6f, 0x6d, 0x06, 31 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32, 32 0x55, 0x54, 0x09, 0x00, 0x03, 0xf1, 0x15, 0x54, 0x5a, 0x38, 0x10, 0x54, 33 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 34 0x88, 0x13, 0x00, 0x00, 0x33, 0x32, 0x82, 0x01, 0x2e, 0x00}; 35 36 // (echo "666666" > 2 && zip -fd test.zip 2 && 37 // cat test.zip | hexdump -v -e '10/1 "0x%02x, " "\n"') 38 const uint8_t kZipEntryWithDataDescriptor[] = { 39 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74, 40 0x2b, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 41 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03, 42 0xf5, 0xe5, 0x57, 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00, 43 0x01, 0x04, 0x8f, 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 44 0x33, 0x03, 0x01, 0x2e, 0x00, 0x50, 0x4b, 0x07, 0x08, 0xb4, 0xa0, 0xf2, 45 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x4b, 0x03, 46 0x04, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0b, 0x74, 0x2b, 0x4c, 0x00, 47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 48 0x00, 0x1c, 0x00, 0x32, 0x55, 0x54, 0x09, 0x00, 0x03, 0xf5, 0xe5, 0x57, 49 0x5a, 0xf2, 0xe5, 0x57, 0x5a, 0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x8f, 50 0x66, 0x05, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x33, 0x33, 0x03, 0x01, 51 0x2e, 0x00, 0xb4, 0xa0, 0xf2, 0x36, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 52 0x00, 0x00}; 53 54 // echo "0123456789" > test1.txt && echo "9876543210" > test2.txt && 55 // gzip -kf test1.txt test2.txt && cat test1.txt.gz test2.txt.gz | 56 // hexdump -v -e '12/1 "0x%02x, " "\n"' 57 const uint8_t kGzipEntryWithMultipleMembers[] = { 58 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84, 0x5a, 0x00, 0x03, 0x74, 0x65, 59 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30, 0x34, 0x32, 60 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1, 0xe5, 0x76, 61 0x40, 0x0b, 0x00, 0x00, 0x00, 0x1f, 0x8b, 0x08, 0x08, 0x77, 0xd5, 0x84, 62 0x5a, 0x00, 0x03, 0x74, 0x65, 0x73, 0x74, 0x32, 0x2e, 0x74, 0x78, 0x74, 63 0x00, 0xb3, 0xb4, 0x30, 0x37, 0x33, 0x35, 0x31, 0x36, 0x32, 0x34, 0xe0, 64 0x02, 0x00, 0x20, 0x9c, 0x5f, 0x89, 0x0b, 0x00, 0x00, 0x00}; 65 66 // echo "0123456789" > test1.txt && gzip -kf test1.txt && cat test1.txt.gz | 67 // hexdump -v -e '12/1 "0x%02x, " "\n"' 68 // And manually insert extra field with two byte length (10) followed by: 69 // echo "extrafield" | hexdump -v -e '12/1 "0x%02x, " "\n"' 70 // Then change the forth byte of array to -x0c to enable the extra field. 71 const uint8_t kGzipEntryWithExtraField[] = { 72 0x1f, 0x8b, 0x08, 0x0c, 0xcf, 0x0e, 0x86, 0x5a, 0x00, 0x03, 73 // Extra field begin 74 0x0A, 0x00, 0x65, 0x78, 0x74, 0x72, 0x61, 0x66, 0x69, 0x65, 0x6c, 0x64, 75 // Extra field end 76 0x74, 0x65, 0x73, 0x74, 0x31, 0x2e, 0x74, 0x78, 0x74, 0x00, 0x33, 0x30, 77 0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 0xe4, 0x02, 0x00, 0xd1, 78 0xe5, 0x76, 0x40, 0x0b, 0x00, 0x00, 0x00}; 79 80 // echo "0123456789" | zlib-flate -compress | 81 // hexdump -v -e '12/1 "0x%02x, " "\n"' 82 const uint8_t kZlibEntry[] = { 83 0x78, 0x9c, 0x33, 0x30, 0x34, 0x32, 0x36, 0x31, 0x35, 0x33, 0xb7, 0xb0, 84 0xe4, 0x02, 0x00, 0x0d, 0x17, 0x02, 0x18}; 85 86 void FindDeflatesInZlibBlocks(const Buffer& src, 87 const vector<ByteExtent>& zlibs, 88 const vector<BitExtent>& deflates) { 89 string tmp_file; 90 ASSERT_TRUE(MakeTempFile(&tmp_file, nullptr)); 91 ScopedPathUnlinker unlinker(tmp_file); 92 auto src_stream = FileStream::Open(tmp_file, false, true); 93 ASSERT_TRUE(src_stream); 94 ASSERT_TRUE(src_stream->Write(src.data(), src.size())); 95 ASSERT_TRUE(src_stream->Close()); 96 97 vector<BitExtent> deflates_out; 98 ASSERT_TRUE(LocateDeflatesInZlibBlocks(tmp_file, zlibs, &deflates_out)); 99 ASSERT_EQ(deflates, deflates_out); 100 } 101 102 void CheckFindPuffLocation(const Buffer& compressed, 103 const vector<BitExtent>& deflates, 104 const vector<ByteExtent>& expected_puffs, 105 uint64_t expected_puff_size) { 106 auto src = MemoryStream::CreateForRead(compressed); 107 vector<ByteExtent> puffs; 108 uint64_t puff_size; 109 ASSERT_TRUE(FindPuffLocations(src, deflates, &puffs, &puff_size)); 110 EXPECT_EQ(puffs, expected_puffs); 111 EXPECT_EQ(puff_size, expected_puff_size); 112 } 113 } // namespace 114 115 // Test Simple Puffing of the source. 116 TEST(UtilsTest, FindPuffLocations1Test) { 117 CheckFindPuffLocation(kDeflates8, kSubblockDeflateExtents8, kPuffExtents8, 118 kPuffs8.size()); 119 } 120 121 TEST(UtilsTest, FindPuffLocations2Test) { 122 CheckFindPuffLocation(kDeflates9, kSubblockDeflateExtents9, kPuffExtents9, 123 kPuffs9.size()); 124 } 125 126 TEST(UtilsTest, LocateDeflatesInZlib) { 127 Buffer zlib_data(kZlibEntry, std::end(kZlibEntry)); 128 vector<ByteExtent> deflates; 129 EXPECT_TRUE(LocateDeflatesInZlib(zlib_data, &deflates)); 130 EXPECT_EQ(static_cast<size_t>(1), deflates.size()); 131 EXPECT_EQ(ByteExtent(2, 13), deflates[0]); 132 } 133 134 TEST(UtilsTest, LocateDeflatesInEmptyZlib) { 135 Buffer empty; 136 vector<ByteExtent> empty_zlibs; 137 vector<BitExtent> empty_deflates; 138 FindDeflatesInZlibBlocks(empty, empty_zlibs, empty_deflates); 139 } 140 141 TEST(UtilsTest, LocateDeflatesInZlibWithInvalidFields) { 142 Buffer zlib_data(kZlibEntry, std::end(kZlibEntry)); 143 auto cmf = zlib_data[0]; 144 auto flag = zlib_data[1]; 145 146 vector<ByteExtent> deflates; 147 zlib_data[0] = cmf & 0xF0; 148 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates)); 149 zlib_data[0] = cmf | (8 << 4); 150 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates)); 151 zlib_data[0] = cmf; // Correct it. 152 153 zlib_data[1] = flag & 0xF0; 154 EXPECT_FALSE(LocateDeflatesInZlib(zlib_data, &deflates)); 155 } 156 157 TEST(UtilsTest, LocateDeflatesInZipArchiveSmoke) { 158 Buffer zip_entries(kZipEntries, std::end(kZipEntries)); 159 vector<ByteExtent> deflates; 160 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates)); 161 EXPECT_EQ(static_cast<size_t>(2), deflates.size()); 162 EXPECT_EQ(ByteExtent(59, 6), deflates[0]); 163 EXPECT_EQ(ByteExtent(124, 6), deflates[1]); 164 } 165 166 TEST(UtilsTest, LocateDeflatesInZipArchiveWithDataDescriptor) { 167 Buffer zip_entries(kZipEntryWithDataDescriptor, 168 std::end(kZipEntryWithDataDescriptor)); 169 vector<ByteExtent> deflates; 170 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates)); 171 EXPECT_EQ(static_cast<size_t>(2), deflates.size()); 172 EXPECT_EQ(ByteExtent(59, 6), deflates[0]); 173 EXPECT_EQ(ByteExtent(140, 6), deflates[1]); 174 } 175 176 TEST(UtilsTest, LocateDeflatesInZipArchiveErrorChecks) { 177 Buffer zip_entries(kZipEntries, std::end(kZipEntries)); 178 // Construct a invalid zip entry whose size overflows. 179 zip_entries[29] = 0xff; 180 vector<ByteExtent> deflates_overflow; 181 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_overflow)); 182 EXPECT_EQ(static_cast<size_t>(1), deflates_overflow.size()); 183 EXPECT_EQ(ByteExtent(124, 6), deflates_overflow[0]); 184 185 zip_entries.resize(128); 186 vector<ByteExtent> deflates_incomplete; 187 EXPECT_TRUE(LocateDeflatesInZipArchive(zip_entries, &deflates_incomplete)); 188 EXPECT_EQ(static_cast<size_t>(0), deflates_incomplete.size()); 189 } 190 191 TEST(UtilsTest, LocateDeflatesInGzip) { 192 Buffer gzip_data(kGzipEntryWithMultipleMembers, 193 std::end(kGzipEntryWithMultipleMembers)); 194 vector<ByteExtent> deflates; 195 EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates)); 196 EXPECT_EQ(static_cast<size_t>(2), deflates.size()); 197 EXPECT_EQ(ByteExtent(20, 13), deflates[0]); 198 EXPECT_EQ(ByteExtent(61, 13), deflates[1]); 199 } 200 201 TEST(UtilsTest, LocateDeflatesInGzipWithExtraField) { 202 Buffer gzip_data(kGzipEntryWithExtraField, 203 std::end(kGzipEntryWithExtraField)); 204 vector<ByteExtent> deflates; 205 EXPECT_TRUE(LocateDeflatesInGzip(gzip_data, &deflates)); 206 EXPECT_EQ(static_cast<size_t>(1), deflates.size()); 207 EXPECT_EQ(ByteExtent(32, 13), deflates[0]); 208 } 209 210 } // namespace puffin 211