forked from OSchip/llvm-project
632 lines
18 KiB
C++
632 lines
18 KiB
C++
//===- unittest/AST/RandstructTest.cpp ------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains tests for Clang's structure field layout randomization.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/*
|
|
* Build this test suite by running `make ASTTests` in the build folder.
|
|
*
|
|
* Run this test suite by running the following in the build folder:
|
|
* ` ./tools/clang/unittests/AST/ASTTests
|
|
* --gtest_filter=RecordLayoutRandomization*`
|
|
*/
|
|
|
|
#include "clang/AST/Randstruct.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "DeclMatcher.h"
|
|
#include "clang/AST/RecordLayout.h"
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
#include "clang/Testing/CommandLineArgs.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
|
|
#include <vector>
|
|
|
|
using namespace clang;
|
|
using namespace clang::ast_matchers;
|
|
using namespace clang::randstruct;
|
|
|
|
using field_names = std::vector<std::string>;
|
|
|
|
constexpr const char Seed[] = "1234567890abcdef";
|
|
|
|
static RecordDecl *getRecordDeclFromAST(const ASTContext &C,
|
|
const std::string &Name) {
|
|
RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match(
|
|
C.getTranslationUnitDecl(), recordDecl(hasName(Name)));
|
|
return RD;
|
|
}
|
|
|
|
static std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) {
|
|
std::vector<std::string> Fields;
|
|
|
|
Fields.reserve(8);
|
|
for (auto *Field : RD->fields())
|
|
Fields.push_back(Field->getNameAsString());
|
|
|
|
return Fields;
|
|
}
|
|
|
|
static bool isSubsequence(const field_names &Seq, const field_names &Subseq) {
|
|
unsigned SeqLen = Seq.size();
|
|
unsigned SubLen = Subseq.size();
|
|
|
|
bool IsSubseq = false;
|
|
for (unsigned I = 0; I < SeqLen; ++I)
|
|
if (Seq[I] == Subseq[0]) {
|
|
IsSubseq = true;
|
|
for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) {
|
|
if (Seq[J + I] != Subseq[J]) {
|
|
IsSubseq = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return IsSubseq;
|
|
}
|
|
|
|
static bool recordsEqual(const std::unique_ptr<ASTUnit> &LHS,
|
|
const std::unique_ptr<ASTUnit> &RHS,
|
|
const std::string &RecordName) {
|
|
const RecordDecl *LHSRD =
|
|
getRecordDeclFromAST(LHS->getASTContext(), RecordName);
|
|
const RecordDecl *RHSRD =
|
|
getRecordDeclFromAST(LHS->getASTContext(), RecordName);
|
|
|
|
return getFieldNamesFromRecord(LHSRD) == getFieldNamesFromRecord(RHSRD);
|
|
}
|
|
|
|
static std::unique_ptr<ASTUnit>
|
|
makeAST(const std::string &SourceCode, bool ExpectError = false,
|
|
std::vector<std::string> RecordNames = std::vector<std::string>()) {
|
|
std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99);
|
|
Args.push_back("-frandomize-layout-seed=" + std::string(Seed));
|
|
|
|
IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer();
|
|
|
|
std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs(
|
|
SourceCode, Args, "input.c", "clang-tool",
|
|
std::make_shared<PCHContainerOperations>(),
|
|
tooling::getClangStripDependencyFileAdjuster(),
|
|
tooling::FileContentMappings(), &IgnoringConsumer);
|
|
|
|
int SeedFileFD = -1;
|
|
llvm::SmallString<256> SeedFilename;
|
|
EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("seed", "rng", SeedFileFD,
|
|
SeedFilename));
|
|
llvm::ToolOutputFile SeedFile(SeedFilename, SeedFileFD);
|
|
SeedFile.os() << Seed << "\n";
|
|
|
|
Args.clear();
|
|
Args = getCommandLineArgsForTesting(Lang_C99);
|
|
Args.push_back("-frandomize-layout-seed-file=" +
|
|
SeedFile.getFilename().str());
|
|
|
|
std::unique_ptr<ASTUnit> ASTFileSeed = tooling::buildASTFromCodeWithArgs(
|
|
SourceCode, Args, "input.c", "clang-tool",
|
|
std::make_shared<PCHContainerOperations>(),
|
|
tooling::getClangStripDependencyFileAdjuster(),
|
|
tooling::FileContentMappings(), &IgnoringConsumer);
|
|
|
|
if (!ExpectError) {
|
|
if (RecordNames.empty())
|
|
RecordNames.push_back("test");
|
|
|
|
for (std::string Name : RecordNames)
|
|
EXPECT_TRUE(recordsEqual(AST, ASTFileSeed, Name));
|
|
}
|
|
|
|
return AST;
|
|
}
|
|
|
|
namespace clang {
|
|
namespace ast_matchers {
|
|
|
|
long declCount(const RecordDecl *RD) {
|
|
return llvm::count_if(RD->decls(), [&](const Decl *D) {
|
|
return isa<FieldDecl>(D) || isa<RecordDecl>(D);
|
|
});
|
|
}
|
|
|
|
#define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest
|
|
|
|
TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) {
|
|
const field_names Seq = {"a", "b", "c", "d"};
|
|
|
|
EXPECT_TRUE(isSubsequence(Seq, {"b", "c"}));
|
|
EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"}));
|
|
EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"}));
|
|
EXPECT_TRUE(isSubsequence(Seq, {"a"}));
|
|
EXPECT_FALSE(isSubsequence(Seq, {"a", "d"}));
|
|
}
|
|
|
|
#define RANDSTRUCT_TEST RecordLayoutRandomization
|
|
|
|
TEST(RANDSTRUCT_TEST, UnmarkedStruct) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
};
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>());
|
|
EXPECT_FALSE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, MarkedNoRandomize) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
} __attribute__((no_randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>());
|
|
EXPECT_FALSE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, MarkedRandomize) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>());
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test __attribute__((randomize_layout));
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
} __attribute__((no_randomize_layout));
|
|
)c",
|
|
true);
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const DiagnosticsEngine &Diags = AST->getDiagnostics();
|
|
|
|
EXPECT_FALSE(Diags.hasFatalErrorOccurred());
|
|
EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
|
|
EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
|
|
EXPECT_EQ(Diags.getNumWarnings(), 1u);
|
|
EXPECT_EQ(Diags.getNumErrors(), 0u);
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
} __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
|
|
)c",
|
|
true);
|
|
|
|
EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const DiagnosticsEngine &Diags = AST->getDiagnostics();
|
|
|
|
EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
|
|
EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
|
|
EXPECT_EQ(Diags.getNumWarnings(), 0u);
|
|
EXPECT_EQ(Diags.getNumErrors(), 1u);
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test3 {
|
|
int bacon;
|
|
long lettuce;
|
|
long long tomato;
|
|
float mayonnaise;
|
|
} __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
|
|
)c",
|
|
true);
|
|
|
|
EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const DiagnosticsEngine &Diags = AST->getDiagnostics();
|
|
|
|
EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
|
|
EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
|
|
EXPECT_EQ(Diags.getNumWarnings(), 0u);
|
|
EXPECT_EQ(Diags.getNumErrors(), 1u);
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a;
|
|
int b;
|
|
int x : 1;
|
|
int y : 1;
|
|
int z : 1;
|
|
int c;
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
const field_names Actual = getFieldNamesFromRecord(RD);
|
|
const field_names Subseq = {"x", "y", "z"};
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_TRUE(isSubsequence(Actual, Subseq));
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a;
|
|
int b;
|
|
int c;
|
|
int d;
|
|
int e;
|
|
int f;
|
|
int g;
|
|
int h;
|
|
char name[];
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->hasFlexibleArrayMember());
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure2) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a;
|
|
int b;
|
|
int c;
|
|
int d;
|
|
int e;
|
|
int f;
|
|
int g;
|
|
int h;
|
|
char name[0];
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_FALSE(RD->hasFlexibleArrayMember());
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure3) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a;
|
|
int b;
|
|
int c;
|
|
int d;
|
|
int e;
|
|
int f;
|
|
int g;
|
|
int h;
|
|
char name[1];
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_FALSE(RD->hasFlexibleArrayMember());
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) {
|
|
std::unique_ptr<ASTUnit> AST =
|
|
makeAST(R"c(
|
|
struct test_struct {
|
|
char a;
|
|
float b[3];
|
|
short c;
|
|
int d;
|
|
} __attribute__((packed, randomize_layout));
|
|
|
|
struct another_struct {
|
|
char a;
|
|
char b[5];
|
|
int c;
|
|
} __attribute__((packed, randomize_layout));
|
|
|
|
struct last_struct {
|
|
char a;
|
|
long long b;
|
|
int c[];
|
|
} __attribute__((packed, randomize_layout));
|
|
)c",
|
|
false,
|
|
std::vector<std::string>(
|
|
{"test_struct", "another_struct", "last_struct"}));
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
// FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
|
|
// Clang's RecordBuilders can actually flesh out the information like
|
|
// alignment, etc.
|
|
{
|
|
const RecordDecl *RD =
|
|
getRecordDeclFromAST(AST->getASTContext(), "test_struct");
|
|
const ASTRecordLayout *Layout =
|
|
&AST->getASTContext().getASTRecordLayout(RD);
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(19, Layout->getSize().getQuantity());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
{
|
|
const RecordDecl *RD =
|
|
getRecordDeclFromAST(AST->getASTContext(), "another_struct");
|
|
const ASTRecordLayout *Layout =
|
|
&AST->getASTContext().getASTRecordLayout(RD);
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(10, Layout->getSize().getQuantity());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
{
|
|
const RecordDecl *RD =
|
|
getRecordDeclFromAST(AST->getASTContext(), "last_struct");
|
|
const ASTRecordLayout *Layout =
|
|
&AST->getASTContext().getASTRecordLayout(RD);
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(9, Layout->getSize().getQuantity());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a : 1;
|
|
int : 0;
|
|
int b : 1;
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
union test {
|
|
int a;
|
|
int b;
|
|
int c;
|
|
int d;
|
|
int e;
|
|
int f;
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_FALSE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int a;
|
|
struct sub_struct {
|
|
int b;
|
|
int c;
|
|
int d;
|
|
int e;
|
|
int f;
|
|
} __attribute__((randomize_layout)) s;
|
|
int f;
|
|
struct {
|
|
int g;
|
|
int h;
|
|
int i;
|
|
int j;
|
|
int k;
|
|
};
|
|
int l;
|
|
union {
|
|
int m;
|
|
int n;
|
|
int o;
|
|
int p;
|
|
int q;
|
|
};
|
|
int r;
|
|
} __attribute__((randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
|
|
bool AnonStructTested = false;
|
|
bool AnonUnionTested = false;
|
|
|
|
for (const Decl *D : RD->decls())
|
|
if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
|
|
if (const auto *Record = FD->getType()->getAs<RecordType>()) {
|
|
RD = Record->getDecl();
|
|
if (RD->isAnonymousStructOrUnion()) {
|
|
// These field orders shouldn't change.
|
|
if (RD->isUnion()) {
|
|
const field_names Expected = {"m", "n", "o", "p", "q"};
|
|
|
|
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
|
AnonUnionTested = true;
|
|
} else {
|
|
const field_names Expected = {"g", "h", "i", "j", "k"};
|
|
|
|
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
|
AnonStructTested = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPECT_TRUE(AnonStructTested);
|
|
EXPECT_TRUE(AnonUnionTested);
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) {
|
|
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
struct test {
|
|
int bacon;
|
|
long lettuce;
|
|
struct { double avocado; char blech; };
|
|
long long tomato;
|
|
union { char toast[8]; unsigned toast_thing; };
|
|
float mayonnaise;
|
|
} __attribute__((randomize_layout));
|
|
|
|
int foo(struct test *t) {
|
|
return t->blech;
|
|
}
|
|
|
|
char *bar(struct test *t) {
|
|
return t->toast;
|
|
}
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
long OriginalDeclCount = declCount(RD);
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
EXPECT_EQ(OriginalDeclCount, declCount(RD));
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) {
|
|
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
typedef void (*func_ptr)();
|
|
|
|
struct test {
|
|
func_ptr a;
|
|
func_ptr b;
|
|
func_ptr c;
|
|
func_ptr d;
|
|
func_ptr e;
|
|
func_ptr f;
|
|
func_ptr g;
|
|
};
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
|
|
EXPECT_TRUE(RD->isRandomized());
|
|
}
|
|
|
|
TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) {
|
|
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
|
typedef void (*func_ptr)();
|
|
|
|
struct test {
|
|
func_ptr a;
|
|
func_ptr b;
|
|
func_ptr c;
|
|
func_ptr d;
|
|
func_ptr e;
|
|
func_ptr f;
|
|
func_ptr g;
|
|
} __attribute__((no_randomize_layout));
|
|
)c");
|
|
|
|
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
|
|
|
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
|
|
|
EXPECT_FALSE(RD->isRandomized());
|
|
}
|
|
|
|
} // namespace ast_matchers
|
|
} // namespace clang
|