[clang] 463790b - [randstruct] Randomize all elements of a record

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 28 12:01:18 PDT 2022


Author: Bill Wendling
Date: 2022-04-28T12:01:11-07:00
New Revision: 463790bfc70e15f9c23b76f1c53e228b644f8bb1

URL: https://github.com/llvm/llvm-project/commit/463790bfc70e15f9c23b76f1c53e228b644f8bb1
DIFF: https://github.com/llvm/llvm-project/commit/463790bfc70e15f9c23b76f1c53e228b644f8bb1.diff

LOG: [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

Added: 
    

Modified: 
    clang/include/clang/AST/Decl.h
    clang/include/clang/AST/Randstruct.h
    clang/lib/AST/Decl.cpp
    clang/lib/AST/Randstruct.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaInit.cpp
    clang/test/Sema/init-randomized-struct.c
    clang/unittests/AST/RandstructTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 04101c3218d71..c2133f4e79a15 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4072,7 +4072,7 @@ class RecordDecl : public TagDecl {
 
   void setIsRandomized(bool V) { RecordDeclBits.IsRandomized = V; }
 
-  void reorderFields(const SmallVectorImpl<Decl *> &Fields);
+  void reorderDecls(const SmallVectorImpl<Decl *> &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 0110e8c355551..d5eaf30919e3a 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 <typename T> class ArrayRef;
 template <typename T> 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<Decl *> Fields,
+bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD,
                               llvm::SmallVectorImpl<Decl *> &FinalOrdering);
 
 } // namespace randstruct

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 050c3ad2dafc6..911479bee5ff9 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<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
 }
 
-void RecordDecl::reorderFields(const SmallVectorImpl<Decl *> &Fields) {
-  std::tie(FirstDecl, LastDecl) = DeclContext::BuildDeclChain(Fields, false);
+void RecordDecl::reorderDecls(const SmallVectorImpl<Decl *> &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 fadc727bbc4ed..99c665f420e66 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<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);
   }
@@ -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<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;

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3f095bc61ebe0..a13a34a9fa59d 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<RandomizeLayoutAttr>() &&
         !Record->isUnion() && !getLangOpts().RandstructSeed.empty() &&
         !Record->isRandomized()) {
-      SmallVector<Decl *, 32> OrigFieldOrdering(Record->fields());
-      SmallVector<Decl *, 32> NewFieldOrdering;
-      if (randstruct::randomizeStructureLayout(
-              Context, Record->getNameAsString(), OrigFieldOrdering,
-              NewFieldOrdering))
-        Record->reorderFields(NewFieldOrdering);
+      SmallVector<Decl *, 32> 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 2a49627ecf106..79c92bde3b770 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<RecordType>()->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<FieldDecl>(D) || isa<RecordDecl>(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<IntegerLiteral>(I))
           return IL->getValue().isZero();

diff  --git a/clang/test/Sema/init-randomized-struct.c b/clang/test/Sema/init-randomized-struct.c
index 60403a91a547b..a708d6a0cfd05 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 134752fa7937e..394900d8f6eb6 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<FieldDecl>(D) || isa<RecordDecl>(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<RandomizeLayoutAttr>());
   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<NoRandomizeLayoutAttr>());
   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<RandomizeLayoutAttr>());
   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<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");
@@ -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<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) {
@@ -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<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


        


More information about the cfe-commits mailing list