Skip to content

Commit

Permalink
[clangd] Also detect corrupt stri table size.
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D91299
  • Loading branch information
sam-mccall committed Nov 19, 2020
1 parent 9cfad5f commit d7747da
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 0 deletions.
9 changes: 9 additions & 0 deletions clang-tools-extra/clangd/index/Serialization.cpp
Expand Up @@ -223,6 +223,15 @@ llvm::Expected<StringTableIn> readStringTable(llvm::StringRef Data) {
if (UncompressedSize == 0) // No compression
Uncompressed = R.rest();
else if (llvm::zlib::isAvailable()) {
// Don't allocate a massive buffer if UncompressedSize was corrupted
// This is effective for sharded index, but not big monolithic ones, as
// once compressed size reaches 4MB nothing can be ruled out.
// Theoretical max ratio from https://zlib.net/zlib_tech.html
constexpr int MaxCompressionRatio = 1032;
if (UncompressedSize / MaxCompressionRatio > R.rest().size())
return error("Bad stri table: uncompress {0} -> {1} bytes is implausible",
R.rest().size(), UncompressedSize);

if (llvm::Error E = llvm::zlib::uncompress(R.rest(), UncompressedStorage,
UncompressedSize))
return std::move(E);
Expand Down
41 changes: 41 additions & 0 deletions clang-tools-extra/clangd/unittests/SerializationTests.cpp
Expand Up @@ -14,6 +14,7 @@
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ScopedPrinter.h"
#include "gmock/gmock.h"
Expand Down Expand Up @@ -383,6 +384,46 @@ TEST(SerializationTest, NoCrashOnBadArraySize) {
EXPECT_EQ(llvm::toString(CorruptParsed.takeError()),
"malformed or truncated include uri");
}

// Check we detect invalid string table size size without allocating it first.
// If this detection fails, the test should allocate a huge array and crash.
TEST(SerializationTest, NoCrashOnBadStringTableSize) {
if (!llvm::zlib::isAvailable()) {
log("skipping test, no zlib");
return;
}

// First, create a valid serialized file.
auto In = readIndexFile(YAML);
ASSERT_FALSE(!In) << In.takeError();
IndexFileOut Out(*In);
Out.Format = IndexFileFormat::RIFF;
std::string Serialized = llvm::to_string(Out);

// Low-level parse it again, we're going to replace the `stri` chunk.
auto Parsed = riff::readFile(Serialized);
ASSERT_FALSE(!Parsed) << Parsed.takeError();
auto Stri = llvm::find_if(Parsed->Chunks, [](riff::Chunk C) {
return C.ID == riff::fourCC("stri");
});
ASSERT_NE(Stri, Parsed->Chunks.end());

// stri consists of an 8 byte uncompressed-size, and then compressed data.
// We'll claim our small amount of data expands to 4GB
std::string CorruptStri =
(llvm::fromHex("ffffffff") + Stri->Data.drop_front(4)).str();
Stri->Data = CorruptStri;
std::string FileDigest = llvm::fromHex("EED8F5EAF25C453C");

// Try to crash rather than hang on large allocation.
ScopedMemoryLimit MemLimit(1000 * 1024 * 1024); // 1GB

std::string CorruptFile = llvm::to_string(*Parsed);
auto CorruptParsed = readIndexFile(CorruptFile);
ASSERT_TRUE(!CorruptParsed);
EXPECT_THAT(llvm::toString(CorruptParsed.takeError()),
testing::HasSubstr("bytes is implausible"));
}
#endif

} // namespace
Expand Down

0 comments on commit d7747da

Please sign in to comment.