#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h" // For StaticAssertDecl
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/SmallVector.h"
namespace clang {
namespace randstruct {
-bool randomizeStructureLayout(const ASTContext &Context, StringRef Name,
- ArrayRef<Decl *> Fields,
+bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD,
SmallVectorImpl<Decl *> &FinalOrdering) {
SmallVector<FieldDecl *, 64> RandomizedFields;
+ SmallVector<Decl *, 8> PostRandomizedFields;
unsigned TotalNumFields = 0;
- for (Decl *D : Fields) {
+ for (Decl *D : RD->decls()) {
++TotalNumFields;
if (auto *FD = dyn_cast<FieldDecl>(D))
RandomizedFields.push_back(FD);
+ else if (isa<StaticAssertDecl>(D) || isa<IndirectFieldDecl>(D))
+ PostRandomizedFields.push_back(D);
else
FinalOrdering.push_back(D);
}
if (RandomizedFields.empty())
return false;
- // Struct might end with a variable-length array or an array of size 0 or 1,
+ // Struct might end with a flexible array or an array of size 0 or 1,
// in which case we don't want to randomize it.
- FieldDecl *VLA = nullptr;
- const auto *CA =
- dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType());
- if ((CA && (CA->getSize().sle(2) || CA->isIncompleteArrayType())) ||
- llvm::any_of(Fields, [](Decl *D) {
- if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
- const Type *FDTy = FD->getType().getTypePtr();
- if (const RecordType *FDTTy = FDTy->getAs<RecordType>())
- return FDTTy->getDecl()->hasFlexibleArrayMember();
- }
- return false;
- }))
- VLA = RandomizedFields.pop_back_val();
-
- std::string Seed = (Context.getLangOpts().RandstructSeed + Name).str();
+ FieldDecl *FlexibleArray =
+ RD->hasFlexibleArrayMember() ? RandomizedFields.pop_back_val() : nullptr;
+ if (!FlexibleArray) {
+ if (const auto *CA =
+ dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType()))
+ if (CA->getSize().sle(2))
+ FlexibleArray = RandomizedFields.pop_back_val();
+ }
+
+ std::string Seed =
+ Context.getLangOpts().RandstructSeed + RD->getNameAsString();
std::seed_seq SeedSeq(Seed.begin(), Seed.end());
std::mt19937 RNG(SeedSeq);
randomizeStructureLayoutImpl(Context, RandomizedFields, RNG);
- if (VLA)
- RandomizedFields.push_back(VLA);
+ // Plorp the randomized decls into the final ordering.
FinalOrdering.insert(FinalOrdering.end(), RandomizedFields.begin(),
RandomizedFields.end());
+ // Add fields that belong towards the end of the RecordDecl.
+ FinalOrdering.insert(FinalOrdering.end(), PostRandomizedFields.begin(),
+ PostRandomizedFields.end());
+
+ // Add back the flexible array.
+ if (FlexibleArray)
+ FinalOrdering.push_back(FlexibleArray);
+
assert(TotalNumFields == FinalOrdering.size() &&
"Decl count has been altered after Randstruct randomization!");
(void)TotalNumFields;
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) {
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) {
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) {
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) {
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, CheckVariableLengthArrayMemberRemainsAtEndOfStructure) {
+TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) {
std::unique_ptr<ASTUnit> AST = makeAST(R"c(
struct test {
int a;
- double b;
- short c;
+ 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) {
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));
}
{
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));
}
{
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));
}
}
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) {
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
- const RecordDecl *RD =
- getRecordDeclFromAST(AST->getASTContext(), "test");
+ const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+ long OriginalDeclCount = declCount(RD);
EXPECT_FALSE(RD->isRandomized());
+ EXPECT_EQ(OriginalDeclCount, declCount(RD));
}
TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
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;
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));
+}
+
} // namespace ast_matchers
} // namespace clang