[clang] 7f31b4a - [randstruct] Enforce using a designated init for a randomized struct

Bill Wendling via cfe-commits cfe-commits at lists.llvm.org
Sat Apr 16 23:25:41 PDT 2022


Author: Bill Wendling
Date: 2022-04-16T23:24:48-07:00
New Revision: 7f31b4a917d8cc13df5eac4b7f0520639c3a8fff

URL: https://github.com/llvm/llvm-project/commit/7f31b4a917d8cc13df5eac4b7f0520639c3a8fff
DIFF: https://github.com/llvm/llvm-project/commit/7f31b4a917d8cc13df5eac4b7f0520639c3a8fff.diff

LOG: [randstruct] Enforce using a designated init for a randomized struct

A randomized structure needs to use a designated or default initializer.
Using a non-designated initializer will result in values being assigned
to the wrong fields.

Differential Revision: https://reviews.llvm.org/D123763

Added: 
    clang/test/Sema/init-randomized-struct.c

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaInit.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a14194d271a71..e79a40d62381f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11602,7 +11602,9 @@ def err_hlsl_pointers_unsupported : Error<
 def err_hlsl_operator_unsupported : Error<
   "the '%select{&|*|->}0' operator is unsupported in HLSL">;
 
-// Layout randomization warning.
+// Layout randomization diagnostics.
+def err_non_designated_init_used : Error<
+  "a randomized struct can only be initialized with a designated initializer">;
 def err_cast_from_randomized_struct : Error<
   "casting from randomized structure pointer type %0 to %1">;
 } // end of sema component.

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index c3bbefbaaed1b..233be547bf118 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -2123,6 +2123,7 @@ 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());
   bool CheckForMissingFields =
     !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts());
   bool HasDesignatedInit = false;
@@ -2172,6 +2173,35 @@ void InitListChecker::CheckStructUnionTypes(
       break;
     }
 
+    // Check if this is an initializer of forms:
+    //
+    //   struct foo f = {};
+    //   struct foo g = {0};
+    //
+    // These are okay for randomized structures. [C99 6.7.8p19]
+    //
+    // Also, if there is only one element in the structure, we allow something
+    // like this, because it's really not randomized in the tranditional sense.
+    //
+    //   struct foo h = {bar};
+    auto IsZeroInitializer = [&](const Expr *I) {
+      if (IList->getNumInits() == 1) {
+        if (NumRecordFields == 1)
+          return true;
+        if (const auto *IL = dyn_cast<IntegerLiteral>(I))
+          return IL->getValue().isZero();
+      }
+      return false;
+    };
+
+    // Don't allow non-designated initializers on randomized structures.
+    if (RD->isRandomized() && !IsZeroInitializer(Init)) {
+      if (!VerifyOnly)
+        SemaRef.Diag(InitLoc, diag::err_non_designated_init_used);
+      hadError = true;
+      break;
+    }
+
     // We've already initialized a member of a union. We're done.
     if (InitializedSomething && DeclType->isUnionType())
       break;

diff  --git a/clang/test/Sema/init-randomized-struct.c b/clang/test/Sema/init-randomized-struct.c
new file mode 100644
index 0000000000000..60403a91a547b
--- /dev/null
+++ b/clang/test/Sema/init-randomized-struct.c
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux -frandomize-layout-seed=1234567890abcdef \
+// RUN:  -verify -fsyntax-only -Werror %s
+
+// Initializing a randomized structure requires a designated initializer,
+// otherwise the element ordering will be off. The only exceptions to this rule
+// are:
+//
+//    - A structure with only one element, and
+//    - A structure initialized with "{0}".
+//
+// These are well-defined situations where the field ordering doesn't affect
+// the result.
+
+typedef void (*func_ptr)();
+
+void foo(void);
+void bar(void);
+void baz(void);
+void gaz(void);
+
+struct test {
+  func_ptr a;
+  func_ptr b;
+  func_ptr c;
+  func_ptr d;
+  func_ptr e;
+  func_ptr f;
+  func_ptr g;
+} __attribute__((randomize_layout));
+
+struct test t1 = {}; // This should be fine per WG14 N2900 (in C23) + our extension handling of it in earlier modes
+struct test t2 = { 0 }; // This should also be fine per C99 6.7.8p19
+struct test t3 = { .f = baz, .b = bar, .g = gaz, .a = foo }; // Okay
+struct test t4 = { .a = foo, bar, baz }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}
+
+struct other_test {
+  func_ptr a;
+  func_ptr b[3];
+  func_ptr c;
+} __attribute__((randomize_layout));
+
+struct other_test t5 = { .a = foo, .b[0] = foo }; // Okay
+struct other_test t6 = { .a = foo, .b[0] = foo, bar, baz }; // Okay
+struct other_test t7 = { .a = foo, .b = { foo, bar, baz } }; // Okay
+struct other_test t8 = { baz, bar, gaz, foo }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}
+struct other_test t9 = { .a = foo, .b[0] = foo, bar, baz, gaz }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}
+
+struct empty_test {
+} __attribute__((randomize_layout));
+
+struct empty_test t10 = {}; // Okay
+
+struct degen_test {
+  func_ptr a;
+} __attribute__((randomize_layout));
+
+struct degen_test t11 = { foo }; // Okay


        


More information about the cfe-commits mailing list