brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.7 KiB · a90665b Raw
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