comparison clang-tools-extra/clangd/unittests/SerializationTests.cpp @ 221:79ff65ed7e25

LLVM12 Original
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 15 Jun 2021 19:15:29 +0900
parents 0572611fdcc8
children c4bab56944e8
comparison
equal deleted inserted replaced
220:42394fc6a535 221:79ff65ed7e25
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 // 6 //
7 //===----------------------------------------------------------------------===// 7 //===----------------------------------------------------------------------===//
8 8
9 #include "Headers.h" 9 #include "Headers.h"
10 #include "RIFF.h"
10 #include "index/Index.h" 11 #include "index/Index.h"
11 #include "index/Serialization.h" 12 #include "index/Serialization.h"
13 #include "support/Logger.h"
12 #include "clang/Tooling/CompilationDatabase.h" 14 #include "clang/Tooling/CompilationDatabase.h"
15 #include "llvm/ADT/ScopeExit.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/Support/Compression.h"
18 #include "llvm/Support/Error.h"
13 #include "llvm/Support/ScopedPrinter.h" 19 #include "llvm/Support/ScopedPrinter.h"
14 #include "gmock/gmock.h" 20 #include "gmock/gmock.h"
15 #include "gtest/gtest.h" 21 #include "gtest/gtest.h"
16 22 #ifdef LLVM_ON_UNIX
17 using ::testing::_; 23 #include <sys/resource.h>
18 using ::testing::AllOf; 24 #endif
25
19 using ::testing::ElementsAre; 26 using ::testing::ElementsAre;
20 using ::testing::Pair; 27 using ::testing::Pair;
21 using ::testing::UnorderedElementsAre; 28 using ::testing::UnorderedElementsAre;
22 using ::testing::UnorderedElementsAreArray; 29 using ::testing::UnorderedElementsAreArray;
23 30
295 EXPECT_NE(SerializedCmd.Filename, Cmd.Filename); 302 EXPECT_NE(SerializedCmd.Filename, Cmd.Filename);
296 EXPECT_NE(SerializedCmd.Heuristic, Cmd.Heuristic); 303 EXPECT_NE(SerializedCmd.Heuristic, Cmd.Heuristic);
297 EXPECT_NE(SerializedCmd.Output, Cmd.Output); 304 EXPECT_NE(SerializedCmd.Output, Cmd.Output);
298 } 305 }
299 } 306 }
307
308 // rlimit is part of POSIX.
309 // ASan uses a lot of address space, so we can't apply strict limits.
310 #if LLVM_ON_UNIX && !LLVM_ADDRESS_SANITIZER_BUILD
311 class ScopedMemoryLimit {
312 struct rlimit OriginalLimit;
313 bool Succeeded = false;
314
315 public:
316 ScopedMemoryLimit(rlim_t Bytes) {
317 if (!getrlimit(RLIMIT_AS, &OriginalLimit)) {
318 struct rlimit NewLimit = OriginalLimit;
319 NewLimit.rlim_cur = Bytes;
320 Succeeded = !setrlimit(RLIMIT_AS, &NewLimit);
321 }
322 if (!Succeeded)
323 log("Failed to set rlimit");
324 }
325
326 ~ScopedMemoryLimit() {
327 if (Succeeded)
328 setrlimit(RLIMIT_AS, &OriginalLimit);
329 }
330 };
331 #else
332 class ScopedMemoryLimit {
333 public:
334 ScopedMemoryLimit(unsigned Bytes) { log("rlimit unsupported"); }
335 };
336 #endif
337
338 // Test that our deserialization detects invalid array sizes without allocating.
339 // If this detection fails, the test should allocate a huge array and crash.
340 TEST(SerializationTest, NoCrashOnBadArraySize) {
341 // This test is tricky because we need to construct a subtly invalid file.
342 // First, create a valid serialized file.
343 auto In = readIndexFile(YAML);
344 ASSERT_FALSE(!In) << In.takeError();
345 IndexFileOut Out(*In);
346 Out.Format = IndexFileFormat::RIFF;
347 std::string Serialized = llvm::to_string(Out);
348
349 // Low-level parse it again and find the `srcs` chunk we're going to corrupt.
350 auto Parsed = riff::readFile(Serialized);
351 ASSERT_FALSE(!Parsed) << Parsed.takeError();
352 auto Srcs = llvm::find_if(Parsed->Chunks, [](riff::Chunk C) {
353 return C.ID == riff::fourCC("srcs");
354 });
355 ASSERT_NE(Srcs, Parsed->Chunks.end());
356
357 // Srcs consists of a sequence of IncludeGraphNodes. In our case, just one.
358 // The node has:
359 // - 1 byte: flags (1)
360 // - varint(stringID): URI
361 // - 8 byte: file digest
362 // - varint: DirectIncludes.length
363 // - repeated varint(stringID): DirectIncludes
364 // We want to set DirectIncludes.length to a huge number.
365 // The offset isn't trivial to find, so we use the file digest.
366 std::string FileDigest = llvm::fromHex("EED8F5EAF25C453C");
367 unsigned Pos = Srcs->Data.find_first_of(FileDigest);
368 ASSERT_NE(Pos, StringRef::npos) << "Couldn't locate file digest";
369 Pos += FileDigest.size();
370
371 // Varints are little-endian base-128 numbers, where the top-bit of each byte
372 // indicates whether there are more. ffffffff0f -> 0xffffffff.
373 std::string CorruptSrcs =
374 (Srcs->Data.take_front(Pos) + llvm::fromHex("ffffffff0f") +
375 "some_random_garbage")
376 .str();
377 Srcs->Data = CorruptSrcs;
378
379 // Try to crash rather than hang on large allocation.
380 ScopedMemoryLimit MemLimit(1000 * 1024 * 1024); // 1GB
381
382 std::string CorruptFile = llvm::to_string(*Parsed);
383 auto CorruptParsed = readIndexFile(CorruptFile);
384 ASSERT_TRUE(!CorruptParsed);
385 EXPECT_EQ(llvm::toString(CorruptParsed.takeError()),
386 "malformed or truncated include uri");
387 }
388
389 // Check we detect invalid string table size size without allocating it first.
390 // If this detection fails, the test should allocate a huge array and crash.
391 TEST(SerializationTest, NoCrashOnBadStringTableSize) {
392 if (!llvm::zlib::isAvailable()) {
393 log("skipping test, no zlib");
394 return;
395 }
396
397 // First, create a valid serialized file.
398 auto In = readIndexFile(YAML);
399 ASSERT_FALSE(!In) << In.takeError();
400 IndexFileOut Out(*In);
401 Out.Format = IndexFileFormat::RIFF;
402 std::string Serialized = llvm::to_string(Out);
403
404 // Low-level parse it again, we're going to replace the `stri` chunk.
405 auto Parsed = riff::readFile(Serialized);
406 ASSERT_FALSE(!Parsed) << Parsed.takeError();
407 auto Stri = llvm::find_if(Parsed->Chunks, [](riff::Chunk C) {
408 return C.ID == riff::fourCC("stri");
409 });
410 ASSERT_NE(Stri, Parsed->Chunks.end());
411
412 // stri consists of an 8 byte uncompressed-size, and then compressed data.
413 // We'll claim our small amount of data expands to 4GB
414 std::string CorruptStri =
415 (llvm::fromHex("ffffffff") + Stri->Data.drop_front(4)).str();
416 Stri->Data = CorruptStri;
417 std::string FileDigest = llvm::fromHex("EED8F5EAF25C453C");
418
419 // Try to crash rather than hang on large allocation.
420 ScopedMemoryLimit MemLimit(1000 * 1024 * 1024); // 1GB
421
422 std::string CorruptFile = llvm::to_string(*Parsed);
423 auto CorruptParsed = readIndexFile(CorruptFile);
424 ASSERT_TRUE(!CorruptParsed);
425 EXPECT_THAT(llvm::toString(CorruptParsed.takeError()),
426 testing::HasSubstr("bytes is implausible"));
427 }
428
300 } // namespace 429 } // namespace
301 } // namespace clangd 430 } // namespace clangd
302 } // namespace clang 431 } // namespace clang