Tests verifying non-zero checksums of zero bytes (#11260)

Summary:
Adds unit tests verifying that a block payload and checksum of all zeros is not falsely considered valid data. The test exhaustively checks that for blocks up to some length (default 20K, more exhaustively 10M) of all zeros do not produce a block checksum of all zeros.

Also small refactoring of an existing checksum test to use parameterized test. (Suggest hiding whitespace changes for review.)

Pull Request resolved: https://github.com/facebook/rocksdb/pull/11260

Test Plan:
this is the test, manual run with
`ROCKSDB_THOROUGH_CHECKSUM_TEST=1` to verify up to 10M.

Reviewed By: hx235

Differential Revision: D43706192

Pulled By: pdillinger

fbshipit-source-id: 95e721c320ca928e7fa2400c2570fb359cc30b1f
oxigraph-8.1.1
Peter Dillinger 2 years ago committed by Facebook GitHub Bot
parent 13357de0c2
commit e01073252b
  1. 53
      table/table_test.cc

@ -2253,6 +2253,12 @@ TEST_P(BlockBasedTableTest, BadChecksumType) {
"Corruption: Corrupt or unsupported checksum type: 123 in test");
}
class BuiltinChecksumTest : public testing::Test,
public testing::WithParamInterface<ChecksumType> {};
INSTANTIATE_TEST_CASE_P(SupportedChecksums, BuiltinChecksumTest,
testing::ValuesIn(GetSupportedChecksums()));
namespace {
std::string ChecksumAsString(const std::string& data,
ChecksumType checksum_type) {
@ -2278,7 +2284,11 @@ std::string ChecksumAsString(std::string* data, char new_last_byte,
// Make sure that checksum values don't change in later versions, even if
// consistent within current version.
TEST_P(BlockBasedTableTest, ChecksumSchemas) {
TEST_P(BuiltinChecksumTest, ChecksumSchemas) {
// Trailing 'x' chars will be replaced by compression type. Specifically,
// the first byte of a block trailer is compression type, which is part of
// the checksum input. This test does not deal with storing or parsing
// checksums from the trailer (next 4 bytes of trailer).
std::string b0 = "x";
std::string b1 = "This is a short block!x";
std::string b2;
@ -2286,7 +2296,6 @@ TEST_P(BlockBasedTableTest, ChecksumSchemas) {
b2.append("This is a long block!");
}
b2.append("x");
// Trailing 'x' will be replaced by compression type
std::string empty;
@ -2294,9 +2303,7 @@ TEST_P(BlockBasedTableTest, ChecksumSchemas) {
char ct2 = kSnappyCompression;
char ct3 = kZSTD;
// Note: first byte of trailer is compression type, last 4 are checksum
for (ChecksumType t : GetSupportedChecksums()) {
ChecksumType t = GetParam();
switch (t) {
case kNoChecksum:
EXPECT_EQ(ChecksumAsString(empty, t), "00000000");
@ -2363,6 +2370,42 @@ TEST_P(BlockBasedTableTest, ChecksumSchemas) {
assert(false);
break;
}
}
TEST_P(BuiltinChecksumTest, ChecksumZeroInputs) {
// Verify that no reasonably sized "all zeros" inputs produce "all zeros"
// output. Otherwise, "wiped" data could appear to be well-formed.
// Assuming essentially random assignment of output values, the likelihood
// of encountering checksum == 0 for an input not specifically crafted is
// 1 in 4 billion.
if (GetParam() == kNoChecksum) {
return;
}
// "Thorough" case is too slow for continouous testing
bool thorough = getenv("ROCKSDB_THOROUGH_CHECKSUM_TEST") != nullptr;
// Verified through 10M
size_t kMaxZerosLen = thorough ? 10000000 : 20000;
std::string zeros(kMaxZerosLen, '\0');
for (size_t len = 0; len < kMaxZerosLen; ++len) {
if (thorough && (len & 0xffffU) == 0) {
fprintf(stderr, "t=%u len=%u\n", (unsigned)GetParam(), (unsigned)len);
}
uint32_t v = ComputeBuiltinChecksum(GetParam(), zeros.data(), len);
if (v == 0U) {
// One exception case:
if (GetParam() == kXXH3 && len == 0) {
// This is not a big deal because assuming the block length is known
// from the block handle, which comes from a checksum-verified block,
// there is nothing to corrupt in a zero-length block. And when there
// is a block trailer with compression byte (as in block-based table),
// zero length checksummed data never arises.
continue;
}
// Only compute this on failure
SCOPED_TRACE("len=" + std::to_string(len));
ASSERT_NE(v, 0U);
}
}
}

Loading…
Cancel
Save