From 463790bfc70e15f9c23b76f1c53e228b644f8bb1 Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Thu, 28 Apr 2022 12:00:47 -0700 Subject: [PATCH] [randstruct] Randomize all elements of a record A record may have more than just FieldDecls in it. If so, then we're likely to drop them if we only randomize the FieldDecls. We need to be careful about anonymous structs/unions. Their fields are made available in the RecordDecl as IndirectFieldDecls, which are listed after the anonymous struct/union. The ordering doesn't appear to be super important, however we place them unrandomized at the end of the RecordDecl just in case. There's also the possiblity of StaticAssertDecls. We also want those at the end. All other non-FieldDecls we place at the top, just in case we get something like: struct foo { enum e { BORK }; enum e a; }; Link: https://github.com/KSPP/linux/issues/185 Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D123958 --- clang/include/clang/AST/Decl.h | 2 +- clang/include/clang/AST/Randstruct.h | 5 +- clang/lib/AST/Decl.cpp | 4 +- clang/lib/AST/Randstruct.cpp | 49 ++++++------ clang/lib/Sema/SemaDecl.cpp | 12 ++- clang/lib/Sema/SemaInit.cpp | 6 +- clang/test/Sema/init-randomized-struct.c | 13 ++++ clang/unittests/AST/RandstructTest.cpp | 126 +++++++++++++++++++++++++++++-- 8 files changed, 175 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 04101c3..c2133f4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4072,7 +4072,7 @@ public: 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 index 0110e8c..d5eaf30 100644 --- 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 @@ class RecordDecl; 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 index 050c3ad..911479b 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -4705,8 +4705,8 @@ bool RecordDecl::isMsStruct(const ASTContext &C) const { 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 index fadc727b..99c665f 100644 --- 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 @@ void randomizeStructureLayoutImpl(const ASTContext &Context, 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 @@ bool randomizeStructureLayout(const ASTContext &Context, StringRef Name, 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 index 3f095bc..a13a34a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18057,16 +18057,14 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, // 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 index 2a49627..79c92bd 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2123,7 +2123,9 @@ void InitListChecker::CheckStructUnionTypes( // 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 @@ void InitListChecker::CheckStructUnionTypes( // 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 index 60403a9..a708d6a 100644 --- a/clang/test/Sema/init-randomized-struct.c +++ b/clang/test/Sema/init-randomized-struct.c @@ -55,3 +55,16 @@ struct degen_test { } __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 index 134752f..394900d 100644 --- a/clang/unittests/AST/RandstructTest.cpp +++ b/clang/unittests/AST/RandstructTest.cpp @@ -132,6 +132,12 @@ makeAST(const std::string &SourceCode, bool ExpectError = false, 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 @@ TEST(RANDSTRUCT_TEST, UnmarkedStruct) { 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 @@ TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { 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 @@ TEST(RANDSTRUCT_TEST, MarkedRandomize) { 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 @@ TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) { 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 @@ TEST(RANDSTRUCT_TEST, CheckVariableLengthArrayMemberRemainsAtEndOfStructure) { 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 @@ 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)); } { @@ -350,9 +428,11 @@ TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { 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 @@ TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { 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 @@ TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) { 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 @@ 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) { @@ -436,8 +521,10 @@ 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; @@ -467,5 +554,34 @@ TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) { 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 -- 2.7.4