diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4072,7 +4072,7 @@ void setIsRandomized(bool V) { RecordDeclBits.IsRandomized = V; } - void reorderFields(const SmallVectorImpl &Fields); + void reorderDecls(const SmallVectorImpl &Decls); /// Determines whether this declaration represents the /// injected class name. diff --git a/clang/include/clang/AST/Randstruct.h b/clang/include/clang/AST/Randstruct.h --- a/clang/include/clang/AST/Randstruct.h +++ b/clang/include/clang/AST/Randstruct.h @@ -15,9 +15,7 @@ #define LLVM_CLANG_AST_RANDSTRUCT_H namespace llvm { -template class ArrayRef; template class SmallVectorImpl; -class StringRef; } // end namespace llvm namespace clang { @@ -28,8 +26,7 @@ namespace randstruct { -bool randomizeStructureLayout(const ASTContext &Context, llvm::StringRef Name, - llvm::ArrayRef Fields, +bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD, llvm::SmallVectorImpl &FinalOrdering); } // namespace randstruct diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4705,8 +4705,8 @@ return hasAttr() || C.getLangOpts().MSBitfields == 1; } -void RecordDecl::reorderFields(const SmallVectorImpl &Fields) { - std::tie(FirstDecl, LastDecl) = DeclContext::BuildDeclChain(Fields, false); +void RecordDecl::reorderDecls(const SmallVectorImpl &Decls) { + std::tie(FirstDecl, LastDecl) = DeclContext::BuildDeclChain(Decls, false); LastDecl->NextInContextAndBits.setPointer(nullptr); setIsRandomized(true); } diff --git a/clang/lib/AST/Randstruct.cpp b/clang/lib/AST/Randstruct.cpp --- a/clang/lib/AST/Randstruct.cpp +++ b/clang/lib/AST/Randstruct.cpp @@ -15,6 +15,8 @@ #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" @@ -170,16 +172,18 @@ namespace clang { namespace randstruct { -bool randomizeStructureLayout(const ASTContext &Context, StringRef Name, - ArrayRef Fields, +bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD, SmallVectorImpl &FinalOrdering) { SmallVector RandomizedFields; + SmallVector PostRandomizedFields; unsigned TotalNumFields = 0; - for (Decl *D : Fields) { + for (Decl *D : RD->decls()) { ++TotalNumFields; if (auto *FD = dyn_cast(D)) RandomizedFields.push_back(FD); + else if (isa(D) || isa(D)) + PostRandomizedFields.push_back(D); else FinalOrdering.push_back(D); } @@ -187,33 +191,36 @@ 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(RandomizedFields.back()->getType()); - if ((CA && (CA->getSize().sle(2) || CA->isIncompleteArrayType())) || - llvm::any_of(Fields, [](Decl *D) { - if (const FieldDecl *FD = dyn_cast(D)) { - const Type *FDTy = FD->getType().getTypePtr(); - if (const RecordType *FDTTy = FDTy->getAs()) - 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(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; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18057,16 +18057,14 @@ // Handle attributes before checking the layout. ProcessDeclAttributeList(S, Record, Attrs); - // Maybe randomize the field order. + // Maybe randomize the record's decls. if (!getLangOpts().CPlusPlus && Record->hasAttr() && !Record->isUnion() && !getLangOpts().RandstructSeed.empty() && !Record->isRandomized()) { - SmallVector OrigFieldOrdering(Record->fields()); - SmallVector NewFieldOrdering; - if (randstruct::randomizeStructureLayout( - Context, Record->getNameAsString(), OrigFieldOrdering, - NewFieldOrdering)) - Record->reorderFields(NewFieldOrdering); + SmallVector NewDeclOrdering; + if (randstruct::randomizeStructureLayout(Context, Record, + NewDeclOrdering)) + Record->reorderDecls(NewDeclOrdering); } // We may have deferred checking for a deleted destructor. Check now. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2123,7 +2123,9 @@ // worthwhile to skip over the rest of the initializer, though. RecordDecl *RD = DeclType->castAs()->getDecl(); RecordDecl::field_iterator FieldEnd = RD->field_end(); - size_t NumRecordFields = std::distance(RD->field_begin(), RD->field_end()); + size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) { + return isa(D) || isa(D); + }); bool CheckForMissingFields = !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); bool HasDesignatedInit = false; @@ -2186,7 +2188,7 @@ // struct foo h = {bar}; auto IsZeroInitializer = [&](const Expr *I) { if (IList->getNumInits() == 1) { - if (NumRecordFields == 1) + if (NumRecordDecls == 1) return true; if (const auto *IL = dyn_cast(I)) return IL->getValue().isZero(); diff --git a/clang/test/Sema/init-randomized-struct.c b/clang/test/Sema/init-randomized-struct.c --- a/clang/test/Sema/init-randomized-struct.c +++ b/clang/test/Sema/init-randomized-struct.c @@ -55,3 +55,16 @@ } __attribute__((randomize_layout)); struct degen_test t11 = { foo }; // Okay + +struct static_assert_test { + int f; + _Static_assert(sizeof(int) == 4, "oh no!"); +} __attribute__((randomize_layout)); + +struct static_assert_test t12 = { 42 }; // Okay + +struct enum_decl_test { + enum e { BORK = 42, FORK = 9 } f; +} __attribute__((randomize_layout)); + +struct enum_decl_test t13 = { BORK }; // Okay diff --git a/clang/unittests/AST/RandstructTest.cpp b/clang/unittests/AST/RandstructTest.cpp --- a/clang/unittests/AST/RandstructTest.cpp +++ b/clang/unittests/AST/RandstructTest.cpp @@ -132,6 +132,12 @@ namespace clang { namespace ast_matchers { +long declCount(const RecordDecl *RD) { + return llvm::count_if(RD->decls(), [&](const Decl *D) { + return isa(D) || isa(D); + }); +} + #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) { @@ -159,9 +165,11 @@ EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); + long OriginalDeclCount = declCount(RD); EXPECT_FALSE(RD->hasAttr()); EXPECT_FALSE(RD->isRandomized()); + EXPECT_EQ(OriginalDeclCount, declCount(RD)); } TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { @@ -177,9 +185,11 @@ EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); + long OriginalDeclCount = declCount(RD); EXPECT_TRUE(RD->hasAttr()); EXPECT_FALSE(RD->isRandomized()); + EXPECT_EQ(OriginalDeclCount, declCount(RD)); } TEST(RANDSTRUCT_TEST, MarkedRandomize) { @@ -195,9 +205,11 @@ EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); + long OriginalDeclCount = declCount(RD); EXPECT_TRUE(RD->hasAttr()); EXPECT_TRUE(RD->isRandomized()); + EXPECT_EQ(OriginalDeclCount, declCount(RD)); } TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) { @@ -280,19 +292,27 @@ 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 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"); @@ -300,8 +320,64 @@ 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 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 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) { @@ -340,9 +416,11 @@ 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)); } { @@ -350,9 +428,11 @@ 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)); } { @@ -360,9 +440,11 @@ 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)); } } @@ -378,8 +460,10 @@ 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) { @@ -396,10 +480,11 @@ 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) { @@ -436,8 +521,10 @@ 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; @@ -467,5 +554,34 @@ EXPECT_TRUE(AnonUnionTested); } +TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) { + std::unique_ptr 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