[randstruct] Randomize all elements of a record
authorBill Wendling <isanbard@gmail.com>
Thu, 28 Apr 2022 19:00:47 +0000 (12:00 -0700)
committerBill Wendling <isanbard@gmail.com>
Thu, 28 Apr 2022 19:01:11 +0000 (12:01 -0700)
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
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

index 04101c3..c2133f4 100644 (file)
@@ -4072,7 +4072,7 @@ public:
 
   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.
index 0110e8c..d5eaf30 100644 (file)
@@ -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
index 050c3ad..911479b 100644 (file)
@@ -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);
 }
index fadc727..99c665f 100644 (file)
@@ -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;
index 3f095bc..a13a34a 100644 (file)
@@ -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.
index 2a49627..79c92bd 100644 (file)
@@ -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();
index 60403a9..a708d6a 100644 (file)
@@ -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
index 134752f..394900d 100644 (file)
@@ -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