diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11602,7 +11602,9 @@ 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 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2123,6 +2123,7 @@ // 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()); bool CheckForMissingFields = !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); bool HasDesignatedInit = false; @@ -2172,6 +2173,35 @@ 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(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 --- /dev/null +++ b/clang/test/Sema/init-randomized-struct.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only -frandomize-layout-seed=1234567890abcdef + +// 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