[clang] 46b2a46 - [randstruct] Use llvm::shuffle to avoid STL impl difference after D121556

Fangrui Song via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 8 18:14:28 PDT 2022


Author: Fangrui Song
Date: 2022-04-08T18:14:21-07:00
New Revision: 46b2a463bdef1bd1d80abee869b09f95ca5a4fc2

URL: https://github.com/llvm/llvm-project/commit/46b2a463bdef1bd1d80abee869b09f95ca5a4fc2
DIFF: https://github.com/llvm/llvm-project/commit/46b2a463bdef1bd1d80abee869b09f95ca5a4fc2.diff

LOG: [randstruct] Use llvm::shuffle to avoid STL impl difference after D121556

This reverts commit 2a2149c754f96376ddf8fed248102dd8e6092a22.
This reverts commit 8d7595be1dd41d7f7470ec90867936ca5e4e0d82.
This reverts commit e2e6899452998932b37f0fa9e66d104a02abe3e5.

If this doesn't work, I'll revert the whole thing.

Added: 
    clang/unittests/AST/RandstructTest.cpp

Modified: 
    clang/lib/AST/Randstruct.cpp
    clang/unittests/AST/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Randstruct.cpp b/clang/lib/AST/Randstruct.cpp
index f6d7ba1c24164..477915eade547 100644
--- a/clang/lib/AST/Randstruct.cpp
+++ b/clang/lib/AST/Randstruct.cpp
@@ -150,14 +150,14 @@ void randomizeStructureLayoutImpl(const ASTContext &Context,
   if (CurrentBitfieldRun)
     Buckets.push_back(std::move(CurrentBitfieldRun));
 
-  std::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
+  llvm::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
 
   // Produce the new ordering of the elements from the Buckets.
   SmallVector<FieldDecl *, 16> FinalOrder;
   for (const std::unique_ptr<Bucket> &B : Buckets) {
     llvm::SmallVectorImpl<FieldDecl *> &RandFields = B->fields();
     if (!B->isBitfieldRun())
-      std::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
+      llvm::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
 
     FinalOrder.insert(FinalOrder.end(), RandFields.begin(), RandFields.end());
   }

diff  --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt
index ab718d192024e..2dcef2d2fca0e 100644
--- a/clang/unittests/AST/CMakeLists.txt
+++ b/clang/unittests/AST/CMakeLists.txt
@@ -25,6 +25,7 @@ add_clang_unittest(ASTTests
   EvaluateAsRValueTest.cpp
   ExternalASTSourceTest.cpp
   NamedDeclPrinterTest.cpp
+  RandstructTest.cpp
   RecursiveASTVisitorTest.cpp
   SizelessTypesTest.cpp
   SourceLocationTest.cpp

diff  --git a/clang/unittests/AST/RandstructTest.cpp b/clang/unittests/AST/RandstructTest.cpp
new file mode 100644
index 0000000000000..932ef7b3c48cc
--- /dev/null
+++ b/clang/unittests/AST/RandstructTest.cpp
@@ -0,0 +1,442 @@
+//===- unittest/AST/RandstructTest.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains tests for Clang's structure field layout randomization.
+//
+//===----------------------------------------------------------------------===//
+
+/*
+ * Build this test suite by running `make ASTTests` in the build folder.
+ *
+ * Run this test suite by running the following in the build folder:
+ * ` ./tools/clang/unittests/AST/ASTTests
+ * --gtest_filter=StructureLayoutRandomization*`
+ */
+
+#include "clang/AST/Randstruct.h"
+#include "gtest/gtest.h"
+
+#include "DeclMatcher.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Testing/CommandLineArgs.h"
+#include "clang/Tooling/Tooling.h"
+
+#include <vector>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::randstruct;
+
+using field_names = std::vector<std::string>;
+
+namespace {
+
+std::unique_ptr<ASTUnit> makeAST(const std::string &SourceCode) {
+  std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99);
+  Args.push_back("-frandomize-layout-seed=1234567890abcdef");
+
+  IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer();
+
+  return tooling::buildASTFromCodeWithArgs(
+      SourceCode, Args, "input.c", "clang-tool",
+      std::make_shared<PCHContainerOperations>(),
+      tooling::getClangStripDependencyFileAdjuster(),
+      tooling::FileContentMappings(), &IgnoringConsumer);
+}
+
+RecordDecl *getRecordDeclFromAST(const ASTContext &C, const std::string &Name) {
+  RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match(
+      C.getTranslationUnitDecl(), recordDecl(hasName(Name)));
+  return RD;
+}
+
+std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) {
+  std::vector<std::string> Fields;
+
+  Fields.reserve(8);
+  for (auto *Field : RD->fields())
+    Fields.push_back(Field->getNameAsString());
+
+  return Fields;
+}
+
+bool isSubsequence(const field_names &Seq, const field_names &Subseq) {
+  unsigned SeqLen = Seq.size();
+  unsigned SubLen = Subseq.size();
+
+  bool IsSubseq = false;
+  for (unsigned I = 0; I < SeqLen; ++I)
+    if (Seq[I] == Subseq[0]) {
+      IsSubseq = true;
+      for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) {
+        if (Seq[J + I] != Subseq[J]) {
+          IsSubseq = false;
+          break;
+        }
+      }
+    }
+
+  return IsSubseq;
+}
+
+} // end anonymous namespace
+
+namespace clang {
+namespace ast_matchers {
+
+#define RANDSTRUCT_TEST_SUITE_TEST StructureLayoutRandomizationTestSuiteTest
+
+TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) {
+  const field_names Seq = {"a", "b", "c", "d"};
+
+  EXPECT_TRUE(isSubsequence(Seq, {"b", "c"}));
+  EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"}));
+  EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"}));
+  EXPECT_TRUE(isSubsequence(Seq, {"a"}));
+  EXPECT_FALSE(isSubsequence(Seq, {"a", "d"}));
+}
+
+#define RANDSTRUCT_TEST StructureLayoutRandomization
+
+TEST(RANDSTRUCT_TEST, UnmarkedStruct) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    };
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+  const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
+
+  EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>());
+  EXPECT_FALSE(RD->isRandomized());
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, MarkedNoRandomize) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    } __attribute__((no_randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+  const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
+
+  EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>());
+  EXPECT_FALSE(RD->isRandomized());
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, MarkedRandomize) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+  const field_names Expected = {"lettuce", "bacon", "mayonnaise", "tomato"};
+
+  EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>());
+  EXPECT_TRUE(RD->isRandomized());
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test __attribute__((randomize_layout));
+    struct test {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    } __attribute__((no_randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  DiagnosticsEngine &Diags = AST->getDiagnostics();
+
+  EXPECT_FALSE(Diags.hasFatalErrorOccurred());
+  EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
+  EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
+  EXPECT_EQ(Diags.getNumWarnings(), 1u);
+  EXPECT_EQ(Diags.getNumErrors(), 0u);
+}
+
+TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test2 {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    } __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
+  )c");
+
+  EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
+
+  DiagnosticsEngine &Diags = AST->getDiagnostics();
+
+  EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
+  EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
+  EXPECT_EQ(Diags.getNumWarnings(), 0u);
+  EXPECT_EQ(Diags.getNumErrors(), 1u);
+}
+
+TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test3 {
+        int bacon;
+        long lettuce;
+        long long tomato;
+        float mayonnaise;
+    } __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
+
+  DiagnosticsEngine &Diags = AST->getDiagnostics();
+
+  EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
+  EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
+  EXPECT_EQ(Diags.getNumWarnings(), 0u);
+  EXPECT_EQ(Diags.getNumErrors(), 1u);
+}
+
+TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test {
+        int a;
+        int b;
+        int x : 1;
+        int y : 1;
+        int z : 1;
+        int c;
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+
+  const field_names Expected = {"a", "b", "c", "x", "y", "z"};
+  const field_names Subseq = {"x", "y", "z"};
+  const field_names Actual = getFieldNamesFromRecord(RD);
+
+  EXPECT_TRUE(isSubsequence(Actual, Subseq));
+  EXPECT_EQ(Expected, Actual);
+}
+
+TEST(RANDSTRUCT_TEST, CheckVariableLengthArrayMemberRemainsAtEndOfStructure) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test {
+        int a;
+        double b;
+        short c;
+        char name[];
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
+  const field_names Expected = {"c", "a", "name", "b"};
+
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test_struct {
+        char a;
+        float b[3];
+        short c;
+        int d;
+    } __attribute__((packed, randomize_layout));
+
+    struct another_struct {
+        char a;
+        char b[5];
+        int c;
+    } __attribute__((packed, randomize_layout));
+
+    struct last_struct {
+        char a;
+        long long b;
+        int c[];
+    } __attribute__((packed, randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
+  // Clang's RecordBuilders can actually flesh out the information like
+  // alignment, etc.
+  {
+    const RecordDecl *RD =
+        getRecordDeclFromAST(AST->getASTContext(), "test_struct");
+    const ASTRecordLayout *Layout =
+        &AST->getASTContext().getASTRecordLayout(RD);
+    const field_names Expected = {"b", "a", "c", "d"};
+
+    EXPECT_EQ(19, Layout->getSize().getQuantity());
+    EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+  }
+
+  {
+    const RecordDecl *RD =
+        getRecordDeclFromAST(AST->getASTContext(), "another_struct");
+    const ASTRecordLayout *Layout =
+        &AST->getASTContext().getASTRecordLayout(RD);
+    const field_names Expected = {"c", "b", "a"};
+
+    EXPECT_EQ(10, Layout->getSize().getQuantity());
+    EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+  }
+
+  {
+    const RecordDecl *RD =
+        getRecordDeclFromAST(AST->getASTContext(), "last_struct");
+    const ASTRecordLayout *Layout =
+        &AST->getASTContext().getASTRecordLayout(RD);
+    const field_names Expected = {"a", "c", "b"};
+
+    EXPECT_EQ(9, Layout->getSize().getQuantity());
+    EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+  }
+}
+
+TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test_struct {
+        int a : 1;
+        int   : 0;
+        int b : 1;
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD =
+      getRecordDeclFromAST(AST->getASTContext(), "test_struct");
+  const field_names Expected = {"b", "a", ""};
+
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    union test_union {
+        int a;
+        int b;
+        int c;
+        int d;
+        int e;
+        int f;
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD =
+      getRecordDeclFromAST(AST->getASTContext(), "test_union");
+  const field_names Expected = {"a", "b", "c", "d", "e", "f"};
+
+  EXPECT_FALSE(RD->isRandomized());
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+}
+
+TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
+  const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
+    struct test_struct {
+        int a;
+        struct sub_struct {
+            int b;
+            int c;
+            int d;
+            int e;
+            int f;
+        } __attribute__((randomize_layout)) s;
+        int f;
+        struct {
+            int g;
+            int h;
+            int i;
+            int j;
+            int k;
+        };
+        int l;
+        union {
+            int m;
+            int n;
+            int o;
+            int p;
+            int q;
+        };
+        int r;
+    } __attribute__((randomize_layout));
+  )c");
+
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+
+  const RecordDecl *RD =
+      getRecordDeclFromAST(AST->getASTContext(), "test_struct");
+  const field_names Expected = {"", "l", "r", "f", "a", "s", ""};
+
+  EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+
+  bool AnonStructTested = false;
+  bool AnonUnionTested = false;
+  for (const Decl *D : RD->decls())
+    if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
+      if (const auto *Record = FD->getType()->getAs<RecordType>()) {
+        RD = Record->getDecl();
+        if (RD->isAnonymousStructOrUnion()) {
+          if (RD->isUnion()) {
+            const field_names Expected = {"m", "n", "o", "p", "q"};
+
+            EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+            AnonUnionTested = true;
+          } else {
+            const field_names Expected = {"g", "h", "i", "j", "k"};
+
+            EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+            AnonStructTested = true;
+          }
+        } else if (RD->isStruct()) {
+          const field_names Expected = {"c", "b", "e", "d", "f"};
+          EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
+        }
+      }
+    }
+
+  EXPECT_TRUE(AnonStructTested);
+  EXPECT_TRUE(AnonUnionTested);
+}
+
+} // namespace ast_matchers
+} // namespace clang


        


More information about the cfe-commits mailing list