1 //===- unittest/AST/RandstructTest.cpp ------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains tests for Clang's structure field layout randomization. 10 // 11 //===----------------------------------------------------------------------===// 12 13 /* 14 * Build this test suite by running `make ASTTests` in the build folder. 15 * 16 * Run this test suite by running the following in the build folder: 17 * ` ./tools/clang/unittests/AST/ASTTests 18 * --gtest_filter=RecordLayoutRandomization*` 19 */ 20 21 #include "clang/AST/Randstruct.h" 22 #include "gtest/gtest.h" 23 24 #include "DeclMatcher.h" 25 #include "clang/AST/RecordLayout.h" 26 #include "clang/ASTMatchers/ASTMatchers.h" 27 #include "clang/Frontend/ASTUnit.h" 28 #include "clang/Testing/CommandLineArgs.h" 29 #include "clang/Tooling/Tooling.h" 30 #include "llvm/Support/ToolOutputFile.h" 31 32 #include <vector> 33 34 using namespace clang; 35 using namespace clang::ast_matchers; 36 using namespace clang::randstruct; 37 38 using field_names = std::vector<std::string>; 39 40 constexpr const char Seed[] = "1234567890abcdef"; 41 42 static RecordDecl *getRecordDeclFromAST(const ASTContext &C, 43 const std::string &Name) { 44 RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match( 45 C.getTranslationUnitDecl(), recordDecl(hasName(Name))); 46 return RD; 47 } 48 49 static std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) { 50 std::vector<std::string> Fields; 51 52 Fields.reserve(8); 53 for (auto *Field : RD->fields()) 54 Fields.push_back(Field->getNameAsString()); 55 56 return Fields; 57 } 58 59 static bool isSubsequence(const field_names &Seq, const field_names &Subseq) { 60 unsigned SeqLen = Seq.size(); 61 unsigned SubLen = Subseq.size(); 62 63 bool IsSubseq = false; 64 for (unsigned I = 0; I < SeqLen; ++I) 65 if (Seq[I] == Subseq[0]) { 66 IsSubseq = true; 67 for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) { 68 if (Seq[J + I] != Subseq[J]) { 69 IsSubseq = false; 70 break; 71 } 72 } 73 } 74 75 return IsSubseq; 76 } 77 78 static bool recordsEqual(const std::unique_ptr<ASTUnit> &LHS, 79 const std::unique_ptr<ASTUnit> &RHS, 80 const std::string &RecordName) { 81 const RecordDecl *LHSRD = 82 getRecordDeclFromAST(LHS->getASTContext(), RecordName); 83 const RecordDecl *RHSRD = 84 getRecordDeclFromAST(LHS->getASTContext(), RecordName); 85 86 return getFieldNamesFromRecord(LHSRD) == getFieldNamesFromRecord(RHSRD); 87 } 88 89 static std::unique_ptr<ASTUnit> 90 makeAST(const std::string &SourceCode, bool ExpectError = false, 91 std::vector<std::string> RecordNames = std::vector<std::string>()) { 92 std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99); 93 Args.push_back("-frandomize-layout-seed=" + std::string(Seed)); 94 95 IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer(); 96 97 std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs( 98 SourceCode, Args, "input.c", "clang-tool", 99 std::make_shared<PCHContainerOperations>(), 100 tooling::getClangStripDependencyFileAdjuster(), 101 tooling::FileContentMappings(), &IgnoringConsumer); 102 103 int SeedFileFD = -1; 104 llvm::SmallString<256> SeedFilename; 105 EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("seed", "rng", SeedFileFD, 106 SeedFilename)); 107 llvm::ToolOutputFile SeedFile(SeedFilename, SeedFileFD); 108 SeedFile.os() << Seed << "\n"; 109 110 Args.clear(); 111 Args = getCommandLineArgsForTesting(Lang_C99); 112 Args.push_back("-frandomize-layout-seed-file=" + 113 SeedFile.getFilename().str()); 114 115 std::unique_ptr<ASTUnit> ASTFileSeed = tooling::buildASTFromCodeWithArgs( 116 SourceCode, Args, "input.c", "clang-tool", 117 std::make_shared<PCHContainerOperations>(), 118 tooling::getClangStripDependencyFileAdjuster(), 119 tooling::FileContentMappings(), &IgnoringConsumer); 120 121 if (!ExpectError) { 122 if (RecordNames.empty()) 123 RecordNames.push_back("test"); 124 125 for (std::string Name : RecordNames) 126 EXPECT_TRUE(recordsEqual(AST, ASTFileSeed, Name)); 127 } 128 129 return AST; 130 } 131 132 namespace clang { 133 namespace ast_matchers { 134 135 long declCount(const RecordDecl *RD) { 136 return llvm::count_if(RD->decls(), [&](const Decl *D) { 137 return isa<FieldDecl>(D) || isa<RecordDecl>(D); 138 }); 139 } 140 141 #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest 142 143 TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) { 144 const field_names Seq = {"a", "b", "c", "d"}; 145 146 EXPECT_TRUE(isSubsequence(Seq, {"b", "c"})); 147 EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"})); 148 EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"})); 149 EXPECT_TRUE(isSubsequence(Seq, {"a"})); 150 EXPECT_FALSE(isSubsequence(Seq, {"a", "d"})); 151 } 152 153 #define RANDSTRUCT_TEST RecordLayoutRandomization 154 155 TEST(RANDSTRUCT_TEST, UnmarkedStruct) { 156 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 157 struct test { 158 int bacon; 159 long lettuce; 160 long long tomato; 161 float mayonnaise; 162 }; 163 )c"); 164 165 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 166 167 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 168 long OriginalDeclCount = declCount(RD); 169 170 EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>()); 171 EXPECT_FALSE(RD->isRandomized()); 172 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 173 } 174 175 TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { 176 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 177 struct test { 178 int bacon; 179 long lettuce; 180 long long tomato; 181 float mayonnaise; 182 } __attribute__((no_randomize_layout)); 183 )c"); 184 185 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 186 187 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 188 long OriginalDeclCount = declCount(RD); 189 190 EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>()); 191 EXPECT_FALSE(RD->isRandomized()); 192 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 193 } 194 195 TEST(RANDSTRUCT_TEST, MarkedRandomize) { 196 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 197 struct test { 198 int bacon; 199 long lettuce; 200 long long tomato; 201 float mayonnaise; 202 } __attribute__((randomize_layout)); 203 )c"); 204 205 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 206 207 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 208 long OriginalDeclCount = declCount(RD); 209 210 EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>()); 211 EXPECT_TRUE(RD->isRandomized()); 212 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 213 } 214 215 TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) { 216 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 217 struct test __attribute__((randomize_layout)); 218 struct test { 219 int bacon; 220 long lettuce; 221 long long tomato; 222 float mayonnaise; 223 } __attribute__((no_randomize_layout)); 224 )c", 225 true); 226 227 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 228 229 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 230 231 EXPECT_FALSE(Diags.hasFatalErrorOccurred()); 232 EXPECT_FALSE(Diags.hasUncompilableErrorOccurred()); 233 EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); 234 EXPECT_EQ(Diags.getNumWarnings(), 1u); 235 EXPECT_EQ(Diags.getNumErrors(), 0u); 236 } 237 238 TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) { 239 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 240 struct test { 241 int bacon; 242 long lettuce; 243 long long tomato; 244 float mayonnaise; 245 } __attribute__((randomize_layout)) __attribute__((no_randomize_layout)); 246 )c", 247 true); 248 249 EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); 250 251 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 252 253 EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); 254 EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); 255 EXPECT_EQ(Diags.getNumWarnings(), 0u); 256 EXPECT_EQ(Diags.getNumErrors(), 1u); 257 } 258 259 TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) { 260 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 261 struct test3 { 262 int bacon; 263 long lettuce; 264 long long tomato; 265 float mayonnaise; 266 } __attribute__((no_randomize_layout)) __attribute__((randomize_layout)); 267 )c", 268 true); 269 270 EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); 271 272 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 273 274 EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); 275 EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); 276 EXPECT_EQ(Diags.getNumWarnings(), 0u); 277 EXPECT_EQ(Diags.getNumErrors(), 1u); 278 } 279 280 TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) { 281 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 282 struct test { 283 int a; 284 int b; 285 int x : 1; 286 int y : 1; 287 int z : 1; 288 int c; 289 } __attribute__((randomize_layout)); 290 )c"); 291 292 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 293 294 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 295 long OriginalDeclCount = declCount(RD); 296 297 const field_names Actual = getFieldNamesFromRecord(RD); 298 const field_names Subseq = {"x", "y", "z"}; 299 300 EXPECT_TRUE(RD->isRandomized()); 301 EXPECT_TRUE(isSubsequence(Actual, Subseq)); 302 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 303 } 304 305 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) { 306 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 307 struct test { 308 int a; 309 int b; 310 int c; 311 int d; 312 int e; 313 int f; 314 int g; 315 int h; 316 char name[]; 317 } __attribute__((randomize_layout)); 318 )c"); 319 320 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 321 322 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 323 long OriginalDeclCount = declCount(RD); 324 325 EXPECT_TRUE(RD->hasFlexibleArrayMember()); 326 EXPECT_TRUE(RD->isRandomized()); 327 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 328 EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name"); 329 } 330 331 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure2) { 332 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 333 struct test { 334 int a; 335 int b; 336 int c; 337 int d; 338 int e; 339 int f; 340 int g; 341 int h; 342 char name[0]; 343 } __attribute__((randomize_layout)); 344 )c"); 345 346 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 347 348 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 349 long OriginalDeclCount = declCount(RD); 350 351 EXPECT_FALSE(RD->hasFlexibleArrayMember()); 352 EXPECT_TRUE(RD->isRandomized()); 353 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 354 EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name"); 355 } 356 357 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure3) { 358 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 359 struct test { 360 int a; 361 int b; 362 int c; 363 int d; 364 int e; 365 int f; 366 int g; 367 int h; 368 char name[1]; 369 } __attribute__((randomize_layout)); 370 )c"); 371 372 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 373 374 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 375 long OriginalDeclCount = declCount(RD); 376 377 EXPECT_FALSE(RD->hasFlexibleArrayMember()); 378 EXPECT_TRUE(RD->isRandomized()); 379 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 380 EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name"); 381 } 382 383 TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { 384 std::unique_ptr<ASTUnit> AST = 385 makeAST(R"c( 386 struct test_struct { 387 char a; 388 float b[3]; 389 short c; 390 int d; 391 } __attribute__((packed, randomize_layout)); 392 393 struct another_struct { 394 char a; 395 char b[5]; 396 int c; 397 } __attribute__((packed, randomize_layout)); 398 399 struct last_struct { 400 char a; 401 long long b; 402 int c[]; 403 } __attribute__((packed, randomize_layout)); 404 )c", 405 false, 406 std::vector<std::string>( 407 {"test_struct", "another_struct", "last_struct"})); 408 409 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 410 411 // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that 412 // Clang's RecordBuilders can actually flesh out the information like 413 // alignment, etc. 414 { 415 const RecordDecl *RD = 416 getRecordDeclFromAST(AST->getASTContext(), "test_struct"); 417 const ASTRecordLayout *Layout = 418 &AST->getASTContext().getASTRecordLayout(RD); 419 long OriginalDeclCount = declCount(RD); 420 421 EXPECT_TRUE(RD->isRandomized()); 422 EXPECT_EQ(19, Layout->getSize().getQuantity()); 423 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 424 } 425 426 { 427 const RecordDecl *RD = 428 getRecordDeclFromAST(AST->getASTContext(), "another_struct"); 429 const ASTRecordLayout *Layout = 430 &AST->getASTContext().getASTRecordLayout(RD); 431 long OriginalDeclCount = declCount(RD); 432 433 EXPECT_TRUE(RD->isRandomized()); 434 EXPECT_EQ(10, Layout->getSize().getQuantity()); 435 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 436 } 437 438 { 439 const RecordDecl *RD = 440 getRecordDeclFromAST(AST->getASTContext(), "last_struct"); 441 const ASTRecordLayout *Layout = 442 &AST->getASTContext().getASTRecordLayout(RD); 443 long OriginalDeclCount = declCount(RD); 444 445 EXPECT_TRUE(RD->isRandomized()); 446 EXPECT_EQ(9, Layout->getSize().getQuantity()); 447 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 448 } 449 } 450 451 TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) { 452 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 453 struct test { 454 int a : 1; 455 int : 0; 456 int b : 1; 457 } __attribute__((randomize_layout)); 458 )c"); 459 460 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 461 462 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 463 long OriginalDeclCount = declCount(RD); 464 465 EXPECT_TRUE(RD->isRandomized()); 466 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 467 } 468 469 TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) { 470 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 471 union test { 472 int a; 473 int b; 474 int c; 475 int d; 476 int e; 477 int f; 478 } __attribute__((randomize_layout)); 479 )c"); 480 481 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 482 483 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 484 long OriginalDeclCount = declCount(RD); 485 486 EXPECT_FALSE(RD->isRandomized()); 487 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 488 } 489 490 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) { 491 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 492 struct test { 493 int a; 494 struct sub_struct { 495 int b; 496 int c; 497 int d; 498 int e; 499 int f; 500 } __attribute__((randomize_layout)) s; 501 int f; 502 struct { 503 int g; 504 int h; 505 int i; 506 int j; 507 int k; 508 }; 509 int l; 510 union { 511 int m; 512 int n; 513 int o; 514 int p; 515 int q; 516 }; 517 int r; 518 } __attribute__((randomize_layout)); 519 )c"); 520 521 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 522 523 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 524 long OriginalDeclCount = declCount(RD); 525 526 EXPECT_TRUE(RD->isRandomized()); 527 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 528 529 bool AnonStructTested = false; 530 bool AnonUnionTested = false; 531 532 for (const Decl *D : RD->decls()) 533 if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) { 534 if (const auto *Record = FD->getType()->getAs<RecordType>()) { 535 RD = Record->getDecl(); 536 if (RD->isAnonymousStructOrUnion()) { 537 // These field orders shouldn't change. 538 if (RD->isUnion()) { 539 const field_names Expected = {"m", "n", "o", "p", "q"}; 540 541 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 542 AnonUnionTested = true; 543 } else { 544 const field_names Expected = {"g", "h", "i", "j", "k"}; 545 546 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 547 AnonStructTested = true; 548 } 549 } 550 } 551 } 552 553 EXPECT_TRUE(AnonStructTested); 554 EXPECT_TRUE(AnonUnionTested); 555 } 556 557 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) { 558 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 559 struct test { 560 int bacon; 561 long lettuce; 562 struct { double avocado; char blech; }; 563 long long tomato; 564 union { char toast[8]; unsigned toast_thing; }; 565 float mayonnaise; 566 } __attribute__((randomize_layout)); 567 568 int foo(struct test *t) { 569 return t->blech; 570 } 571 572 char *bar(struct test *t) { 573 return t->toast; 574 } 575 )c"); 576 577 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 578 579 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 580 long OriginalDeclCount = declCount(RD); 581 582 EXPECT_TRUE(RD->isRandomized()); 583 EXPECT_EQ(OriginalDeclCount, declCount(RD)); 584 } 585 586 TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) { 587 const std::unique_ptr<ASTUnit> AST = makeAST(R"c( 588 typedef void (*func_ptr)(); 589 590 struct test { 591 func_ptr a; 592 func_ptr b; 593 func_ptr c; 594 func_ptr d; 595 func_ptr e; 596 func_ptr f; 597 func_ptr g; 598 }; 599 )c"); 600 601 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 602 603 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 604 605 EXPECT_TRUE(RD->isRandomized()); 606 } 607 608 TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) { 609 const std::unique_ptr<ASTUnit> AST = makeAST(R"c( 610 typedef void (*func_ptr)(); 611 612 struct test { 613 func_ptr a; 614 func_ptr b; 615 func_ptr c; 616 func_ptr d; 617 func_ptr e; 618 func_ptr f; 619 func_ptr g; 620 } __attribute__((no_randomize_layout)); 621 )c"); 622 623 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 624 625 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 626 627 EXPECT_FALSE(RD->isRandomized()); 628 } 629 630 } // namespace ast_matchers 631 } // namespace clang 632