631 lines · cpp
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-exception6//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/ASTTests18 * --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 34using namespace clang;35using namespace clang::ast_matchers;36using namespace clang::randstruct;37 38using field_names = std::vector<std::string>;39 40constexpr const char Seed[] = "1234567890abcdef";41 42static 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 49static 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 59static 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 78static 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 89static std::unique_ptr<ASTUnit>90makeAST(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 132namespace clang {133namespace ast_matchers {134 135long 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 RecordLayoutRandomizationTestSuiteTest142 143TEST(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 RecordLayoutRandomization154 155TEST(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 175TEST(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 195TEST(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 215TEST(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 238TEST(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 259TEST(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 280TEST(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 305TEST(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 331TEST(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 357TEST(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 383TEST(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 that412 // Clang's RecordBuilders can actually flesh out the information like413 // 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 451TEST(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 469TEST(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 490TEST(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 *RD = FD->getType()->getAsRecordDecl()) {535 if (RD->isAnonymousStructOrUnion()) {536 // These field orders shouldn't change.537 if (RD->isUnion()) {538 const field_names Expected = {"m", "n", "o", "p", "q"};539 540 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));541 AnonUnionTested = true;542 } else {543 const field_names Expected = {"g", "h", "i", "j", "k"};544 545 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));546 AnonStructTested = true;547 }548 }549 }550 }551 552 EXPECT_TRUE(AnonStructTested);553 EXPECT_TRUE(AnonUnionTested);554}555 556TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) {557 std::unique_ptr<ASTUnit> AST = makeAST(R"c(558 struct test {559 int bacon;560 long lettuce;561 struct { double avocado; char blech; };562 long long tomato;563 union { char toast[8]; unsigned toast_thing; };564 float mayonnaise;565 } __attribute__((randomize_layout));566 567 int foo(struct test *t) {568 return t->blech;569 }570 571 char *bar(struct test *t) {572 return t->toast;573 }574 )c");575 576 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());577 578 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");579 long OriginalDeclCount = declCount(RD);580 581 EXPECT_TRUE(RD->isRandomized());582 EXPECT_EQ(OriginalDeclCount, declCount(RD));583}584 585TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) {586 const std::unique_ptr<ASTUnit> AST = makeAST(R"c(587 typedef void (*func_ptr)();588 589 struct test {590 func_ptr a;591 func_ptr b;592 func_ptr c;593 func_ptr d;594 func_ptr e;595 func_ptr f;596 func_ptr g;597 };598 )c");599 600 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());601 602 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");603 604 EXPECT_TRUE(RD->isRandomized());605}606 607TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) {608 const std::unique_ptr<ASTUnit> AST = makeAST(R"c(609 typedef void (*func_ptr)();610 611 struct test {612 func_ptr a;613 func_ptr b;614 func_ptr c;615 func_ptr d;616 func_ptr e;617 func_ptr f;618 func_ptr g;619 } __attribute__((no_randomize_layout));620 )c");621 622 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());623 624 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");625 626 EXPECT_FALSE(RD->isRandomized());627}628 629} // namespace ast_matchers630} // namespace clang631