// Copyright 2005 and onwards Google Inc. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SNAPPY_FLAG(bool, snappy_dump_decompression_table, false, "If true, we print the decompression table during tests.");
namespace snappy {
namespace {
#if HAVE_FUNC_MMAP && HAVE_FUNC_SYSCONF
// To test against code that reads beyond its input, this class copies a // string to a newly allocated group of pages, the last of which // is made unreadable via mprotect. Note that we need to allocate the // memory with mmap(), as POSIX allows mprotect() only on memory allocated // with mmap(), and some malloc/posix_memalign implementations expect to // be able to read previously allocated memory while doing heap allocations. class DataEndingAtUnreadablePage { public: explicit DataEndingAtUnreadablePage(const std::string& s) { const size_t page_size = sysconf(_SC_PAGESIZE); const size_t size = s.size(); // Round up space for string to a multiple of page_size.
size_t space_for_string = (size + page_size - 1) & ~(page_size - 1);
alloc_size_ = space_for_string + page_size;
mem_ = mmap(NULL, alloc_size_,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
CHECK_NE(MAP_FAILED, mem_);
protected_page_ = reinterpret_cast<char*>(mem_) + space_for_string; char* dst = protected_page_ - size;
std::memcpy(dst, s.data(), size);
data_ = dst;
size_ = size; // Make guard page unreadable.
CHECK_EQ(0, mprotect(protected_page_, page_size, PROT_NONE));
}
// Test that data compressed by a compressor that does not // obey block sizes is uncompressed properly. void VerifyNonBlockedCompression(const std::string& input) { if (input.length() > snappy::kBlockSize) { // We cannot test larger blocks than the maximum block size, obviously. return;
}
// Expand the input so that it is at least K times as big as block size
std::string Expand(const std::string& input) { staticconstint K = 3;
std::string data = input; while (data.size() < K * snappy::kBlockSize) {
data += input;
} return data;
}
int Verify(const std::string& input) {
VLOG(1) << "Verifying input of size " << input.size();
// Compress using string based routines constint result = VerifyString(input);
// Compress using `iovec`-based routines.
CHECK_EQ(VerifyIOVecSource(input), result);
// Verify using sink based routines
VerifyStringSink(input);
// This test checks to ensure that snappy doesn't coredump if it gets // corrupted data.
TEST(CorruptedTest, VerifyCorrupted) {
std::string source = "making sure we don't crash with corrupted input";
VLOG(1) << source;
std::string dest;
std::string uncmp;
snappy::Compress(source.data(), source.size(), &dest);
// Mess around with the data. It's hard to simulate all possible // corruptions; this is just one example ...
CHECK_GT(dest.size(), 3);
dest[1]--;
dest[3]++; // this really ought to fail.
CHECK(!IsValidCompressedBuffer(dest));
CHECK(!Uncompress(dest, &uncmp));
// This is testing for a security bug - a buffer that decompresses to 100k // but we lie in the snappy header and only reserve 0 bytes of memory :)
source.resize(100000); for (char& source_char : source) {
source_char = 'A';
}
snappy::Compress(source.data(), source.size(), &dest);
dest[0] = dest[1] = dest[2] = dest[3] = 0;
CHECK(!IsValidCompressedBuffer(dest));
CHECK(!Uncompress(dest, &uncmp));
if (sizeof(void *) == 4) { // Another security check; check a crazy big length can't DoS us with an // over-allocation. // Currently this is done only for 32-bit builds. On 64-bit builds, // where 3 GB might be an acceptable allocation size, Uncompress() // attempts to decompress, and sometimes causes the test to run out of // memory.
dest[0] = dest[1] = dest[2] = dest[3] = '\xff'; // This decodes to a really large size, i.e., about 3 GB.
dest[4] = 'k';
CHECK(!IsValidCompressedBuffer(dest));
CHECK(!Uncompress(dest, &uncmp));
} else {
LOG(WARNING) << "Crazy decompression lengths not checked on 64-bit build";
}
// This decodes to about 2 MB; much smaller, but should still fail.
dest[0] = dest[1] = dest[2] = '\xff';
dest[3] = 0x00;
CHECK(!IsValidCompressedBuffer(dest));
CHECK(!Uncompress(dest, &uncmp));
// try reading stuff in from a bad file. for (int i = 1; i <= 3; ++i) {
std::string data =
ReadTestDataFile(StrFormat("baddata%d.snappy", i).c_str(), 0);
std::string uncmp; // check that we don't return a crazy length
size_t ulen;
CHECK(!snappy::GetUncompressedLength(data.data(), data.size(), &ulen)
|| (ulen < (1<<20)));
uint32_t ulen2;
snappy::ByteArraySource source(data.data(), data.size());
CHECK(!snappy::GetUncompressedLength(&source, &ulen2) ||
(ulen2 < (1<<20)));
CHECK(!IsValidCompressedBuffer(data));
CHECK(!Uncompress(data, &uncmp));
}
}
// Helper routines to construct arbitrary compressed strings. // These mirror the compression code in snappy.cc, but are copied // here so that we can bypass some limitations in the how snappy.cc // invokes these routines. void AppendLiteral(std::string* dst, const std::string& literal) { if (literal.empty()) return; int n = literal.size() - 1; if (n < 60) { // Fit length in tag byte
dst->push_back(0 | (n << 2));
} else { // Encode in upcoming bytes char number[4]; int count = 0; while (n > 0) {
number[count++] = n & 0xff;
n >>= 8;
}
dst->push_back(0 | ((59+count) << 2));
*dst += std::string(number, count);
}
*dst += literal;
}
void AppendCopy(std::string* dst, int offset, int length) { while (length > 0) { // Figure out how much to copy in one shot int to_copy; if (length >= 68) {
to_copy = 64;
} elseif (length > 64) {
to_copy = 60;
} else {
to_copy = length;
}
length -= to_copy;
// Regression test for cr/345340892.
TEST(Snappy, AppendSelfPatternExtensionEdgeCases) {
Verify("abcabcabcabcabcabcab");
Verify("abcabcabcabcabcabcab0123456789ABCDEF");
constexpr int num_ops = 20000; for (int i = 0; i < num_ops; ++i) { if ((i % 1000) == 0) {
VLOG(0) << "Random op " << i << " of " << num_ops;
}
std::string x;
size_t len = uniform_4k(rng); if (i < 100) {
len = 65536 + uniform_64k(rng);
} while (x.size() < len) { int run_len = 1; if (one_in_ten(rng)) { int skewed_bits = uniform_0_to_8(rng); // int is guaranteed to hold at least 16 bits, this uses at most 8 bits.
std::uniform_int_distribution<int> skewed_low(0,
(1 << skewed_bits) - 1);
run_len = skewed_low(rng);
} char c = static_cast<char>(uniform_byte(rng)); if (i >= 100) { int skewed_bits = uniform_0_to_3(rng); // int is guaranteed to hold at least 16 bits, this uses at most 3 bits.
std::uniform_int_distribution<int> skewed_low(0,
(1 << skewed_bits) - 1);
c = static_cast<char>(skewed_low(rng));
} while (run_len-- > 0 && x.size() < len) {
x.push_back(c);
}
}
Verify(x);
}
}
TEST(Snappy, FourByteOffset) { // The new compressor cannot generate four-byte offsets since // it chops up the input into 32KB pieces. So we hand-emit the // copy manually.
// The two fragments that make up the input string.
std::string fragment1 = "012345689abcdefghijklmnopqrstuvwxyz";
std::string fragment2 = "some other string";
// How many times each fragment is emitted. constint n1 = 2; constint n2 = 100000 / fragment2.size(); const size_t length = n1 * fragment1.size() + n2 * fragment2.size();
// A literal whose output crosses three blocks. // [ab] [c] [123 ] [ ] [ ]
AppendLiteral(&compressed, "abc123");
// A copy whose output crosses two blocks (source and destination // segments marked). // [ab] [c] [1231] [23 ] [ ] // ^--^ --
AppendCopy(&compressed, 3, 3);
// A copy where the input is, at first, in the block before the output: // // [ab] [c] [1231] [231231 ] [ ] // ^--- ^--- // Then during the copy, the pointers move such that the input and // output pointers are in the same block: // // [ab] [c] [1231] [23123123] [ ] // ^- ^- // And then they move again, so that the output pointer is no longer // in the same block as the input pointer: // [ab] [c] [1231] [23123123] [123 ] // ^-- ^--
AppendCopy(&compressed, 6, 9);
// Finally, a copy where the input is from several blocks back, // and it also crosses three blocks: // // [ab] [c] [1231] [23123123] [123b ] // ^ ^ // [ab] [c] [1231] [23123123] [123bc ] // ^ ^ // [ab] [c] [1231] [23123123] [123bc12 ] // ^- ^-
AppendCopy(&compressed, 17, 4);
TEST(Snappy, ReadPastEndOfBuffer) { // Check that we do not read past end of input
// Make a compressed string that ends with a single-byte literal
std::string compressed;
Varint::Append32(&compressed, 1);
AppendLiteral(&compressed, "x");
TEST(Snappy, FindMatchLength) { // Exercise all different code paths through the function. // 64-bit version:
// Hit s1_limit in 64-bit loop, hit s1_limit in single-character loop.
EXPECT_EQ(6, TestFindMatchLength("012345", "012345", 6));
EXPECT_EQ(11, TestFindMatchLength("01234567abc", "01234567abc", 11));
// Hit s1_limit in 64-bit loop, find a non-match in single-character loop.
EXPECT_EQ(9, TestFindMatchLength("01234567abc", "01234567axc", 9));
for (int i = 0; i < kNumTrials; ++i) {
std::string s, t; char a = static_cast<char>(uniform_byte(rng)); char b = static_cast<char>(uniform_byte(rng)); while (!one_in_typical_length(rng)) {
s.push_back(one_in_two(rng) ? a : b);
t.push_back(one_in_two(rng) ? a : b);
}
DataEndingAtUnreadablePage u(s);
DataEndingAtUnreadablePage v(t);
size_t matched = TestFindMatchLength(u.data(), v.data(), t.size()); if (matched == t.size()) {
EXPECT_EQ(s, t);
} else {
EXPECT_NE(s[matched], t[matched]); for (size_t j = 0; j < matched; ++j) {
EXPECT_EQ(s[j], t[j]);
}
}
}
}
uint16_t MakeEntry(unsignedint extra, unsignedint len, unsignedint copy_offset) { // Check that all of the fields fit within the allocated space
assert(extra == (extra & 0x7)); // At most 3 bits
assert(copy_offset == (copy_offset & 0x7)); // At most 3 bits
assert(len == (len & 0x7f)); // At most 7 bits return len | (copy_offset << 8) | (extra << 11);
}
// Check that the decompression table is correct, and optionally print out // the computed one.
TEST(Snappy, VerifyCharTable) { using snappy::internal::LITERAL; using snappy::internal::COPY_1_BYTE_OFFSET; using snappy::internal::COPY_2_BYTE_OFFSET; using snappy::internal::COPY_4_BYTE_OFFSET; using snappy::internal::char_table;
uint16_t dst[256];
// Place invalid entries in all places to detect missing initialization int assigned = 0; for (int i = 0; i < 256; ++i) {
dst[i] = 0xffff;
}
// Small LITERAL entries. We store (len-1) in the top 6 bits. for (uint8_t len = 1; len <= 60; ++len) {
dst[LITERAL | ((len - 1) << 2)] = MakeEntry(0, len, 0);
assigned++;
}
// Large LITERAL entries. We use 60..63 in the high 6 bits to // encode the number of bytes of length info that follow the opcode. for (uint8_t extra_bytes = 1; extra_bytes <= 4; ++extra_bytes) { // We set the length field in the lookup table to 1 because extra // bytes encode len-1.
dst[LITERAL | ((extra_bytes + 59) << 2)] = MakeEntry(extra_bytes, 1, 0);
assigned++;
}
// COPY_1_BYTE_OFFSET. // // The tag byte in the compressed data stores len-4 in 3 bits, and // offset/256 in 3 bits. offset%256 is stored in the next byte. // // This format is used for length in range [4..11] and offset in // range [0..2047] for (uint8_t len = 4; len < 12; ++len) { for (uint16_t offset = 0; offset < 2048; offset += 256) {
uint8_t offset_high = static_cast<uint8_t>(offset >> 8);
dst[COPY_1_BYTE_OFFSET | ((len - 4) << 2) | (offset_high << 5)] =
MakeEntry(1, len, offset_high);
assigned++;
}
}
// COPY_2_BYTE_OFFSET. // Tag contains len-1 in top 6 bits, and offset in next two bytes. for (uint8_t len = 1; len <= 64; ++len) {
dst[COPY_2_BYTE_OFFSET | ((len - 1) << 2)] = MakeEntry(2, len, 0);
assigned++;
}
// COPY_4_BYTE_OFFSET. // Tag contents len-1 in top 6 bits, and offset in next four bytes. for (uint8_t len = 1; len <= 64; ++len) {
dst[COPY_4_BYTE_OFFSET | ((len - 1) << 2)] = MakeEntry(4, len, 0);
assigned++;
}
// Check that each entry was initialized exactly once.
EXPECT_EQ(256, assigned) << "Assigned only " << assigned << " of 256"; for (int i = 0; i < 256; ++i) {
EXPECT_NE(0xffff, dst[i]) << "Did not assign byte " << i;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.