diff --git a/clang/include/clang/AST/RandstructSeed.h b/clang/include/clang/AST/RandstructSeed.h index 483dda27b14e5..eefdd8916f4b6 100644 --- a/clang/include/clang/AST/RandstructSeed.h +++ b/clang/include/clang/AST/RandstructSeed.h @@ -3,5 +3,6 @@ #include namespace clang { extern std::string RandstructSeed; +extern bool RandstructAutoSelect; } #endif diff --git a/clang/include/clang/AST/RecordFieldReorganizer.h b/clang/include/clang/AST/RecordFieldReorganizer.h index 1f2562b5e389c..ef673e8c75b73 100644 --- a/clang/include/clang/AST/RecordFieldReorganizer.h +++ b/clang/include/clang/AST/RecordFieldReorganizer.h @@ -37,6 +37,9 @@ class RecordFieldReorganizer { }; class Randstruct : public RecordFieldReorganizer { +public: +/// Determines if the Record can be safely and easily randomized based on certain criteria (see implementation). + static bool isTriviallyRandomizable(const RecordDecl *D); protected: virtual void reorganize(const ASTContext &C, const RecordDecl *D, SmallVector &NewOrder) const override; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8099366d19a0b..b71af0c1b27b3 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3208,3 +3208,10 @@ def RandomizeLayout : InheritableAttr { let Subjects = SubjectList<[Record]>; let Documentation = [ClangRandstructDocs]; } + +def NoRandomizeLayout : InheritableAttr { + let Spellings = [GCC<"no_randomize_layout">, Declspec<"no_randomize_layout">, + Keyword<"no_randomize_layout">]; + let Subjects = SubjectList<[Record]>; + let Documentation = [ClangRandstructDocs]; +} diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a6f109f41d6da..d3a40cd481589 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4099,10 +4099,24 @@ When compiled without ``-fobjc-arc``, this attribute is ignored. def ClangRandstructDocs : Documentation { let Category = DocCatVariable; + let Heading = "randomize_layout, no_randomize_layout"; let Content = [{ -The attribute ``randomize_layout`` can be applied to the declaration of -a record. ``randomize_layout`` instructs the compiler to randomize the memory layout +The attributes ``randomize_layout`` and ``no_randomize_layout`` can be applied +to a record. + +``randomize_layout`` instructs the compiler to randomize the memory layout of the member variables of the record. + +Conversely, ``no_randomize_layout`` is used to indicate that if using the +automatic strucuture selection feature of the Randstruct implementation, the +compiler should not shuffle the members of the record. + +In the event that a record is labeled with both attributes, the compiler will +emit a warning indicating that these two cannot be used on the same record. +The default behavior in this case is to not randomize the struct, as the +attribute ``no_randomize_layout`` takes precedence over ``randomize_layout``. +This is implementation defined behavior. + .. code-block:: c // Indicates that this struct should be randomized by Randstruct implementation. @@ -4111,5 +4125,19 @@ of the member variables of the record. char *b; char *c; }__attribute__((randomize_layout)); + + // Indicates that this struct should NOT be randomized by Randstruct implementation. + struct s { + char *a; + char *b; + char *c; + }__attribute__((no_randomize_layout)); + + // Emits compiler warning. Struct is NOT randomized by Randstruct implementation. + struct s { + char *a; + char *b; + char *c; + }__attribute__((randomize_layout)) __attribute__((no_randomize_layout)); }]; } diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 406325d3677a2..e0970a7b1458f 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -345,4 +345,7 @@ def warn_unnecessary_packed : Warning< "packed attribute is unnecessary for %0">, InGroup, DefaultIgnore; def warn_randomize_attr_union : Warning< "union declared with 'randomize_layout' attribute">, InGroup>; +def warn_randomize_attr_conflict : Warning< + "struct declared with 'randomize_layout' and 'no_randomize_layout' attributes; " + "attribute 'no_randomize_layout' takes precedence">, InGroup>; } diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index b86c776e829e4..d4736df95f7fb 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1751,6 +1751,9 @@ def freroll_loops : Flag<["-"], "freroll-loops">, Group, HelpText<"Turn on loop reroller">, Flags<[CC1Option]>; def fno_reroll_loops : Flag<["-"], "fno-reroll-loops">, Group, HelpText<"Turn off loop reroller">; +def randstruct_auto : Flag<["-"], "randstruct-auto">, + HelpText<"Enable automatic structure selection for field randomization; " + "disabled with no_randomize_layout">, Flags<[CC1Option]>; def ftrigraphs : Flag<["-"], "ftrigraphs">, Group, HelpText<"Process trigraph sequences">, Flags<[CC1Option]>; def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group, diff --git a/clang/lib/AST/RecordFieldReorganizer.cpp b/clang/lib/AST/RecordFieldReorganizer.cpp index 52bda0dabff8b..0b3ee20db0d79 100644 --- a/clang/lib/AST/RecordFieldReorganizer.cpp +++ b/clang/lib/AST/RecordFieldReorganizer.cpp @@ -25,6 +25,7 @@ namespace clang { std::string RandstructSeed = ""; +bool RandstructAutoSelect = false; void RecordFieldReorganizer::reorganizeFields(const ASTContext &C, const RecordDecl *D) const { @@ -37,7 +38,6 @@ void RecordFieldReorganizer::reorganizeFields(const ASTContext &C, mutateGuard.insert(f); fields.push_back(f); } - // Now allow subclass implementations to reorder the fields reorganize(C, D, fields); @@ -52,7 +52,6 @@ void RecordFieldReorganizer::reorganizeFields(const ASTContext &C, commit(D, fields); } - void RecordFieldReorganizer::commit( const RecordDecl *D, SmallVectorImpl &NewFieldOrder) const { Decl *First, *Last; @@ -254,5 +253,12 @@ void Randstruct::reorganize(const ASTContext &C, const RecordDecl *D, SmallVector randomized = perfrandomize(C, NewOrder); NewOrder = randomized; } - +bool Randstruct::isTriviallyRandomizable(const RecordDecl *D) { + for (auto f : D->fields()){ + //If an element of the structure does not have a + //function type is not a function pointer + if(f->getFunctionType() == nullptr){ return false; } + } + return true; +} } // namespace clang diff --git a/clang/lib/AST/RecordLayoutBuilder.cpp b/clang/lib/AST/RecordLayoutBuilder.cpp index 11f96294fdc53..a64a6dd7122f0 100644 --- a/clang/lib/AST/RecordLayoutBuilder.cpp +++ b/clang/lib/AST/RecordLayoutBuilder.cpp @@ -2987,18 +2987,23 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { if (Entry) return *Entry; const ASTRecordLayout *NewEntry = nullptr; - + bool ShouldBeRandomized = D->getAttr() != nullptr; - if (ShouldBeRandomized) { - // There is no technical benefit to randomizing the fields of a union - // since they all share the same offset of zero. - if (D->isUnion()) { + bool NotToBeRandomized = D->getAttr() != nullptr; + bool AutoSelectable = RandstructAutoSelect && Randstruct::isTriviallyRandomizable(D); + + if (ShouldBeRandomized && NotToBeRandomized) { + getDiagnostics().Report(D->getLocation(), diag::warn_randomize_attr_conflict); + } + + if (ShouldBeRandomized && D->isUnion()) { getDiagnostics().Report(D->getLocation(), diag::warn_randomize_attr_union); - } - else { - Randstruct randstruct; - randstruct.reorganizeFields(*this,D); - } + NotToBeRandomized = true; + } + + if (!NotToBeRandomized && (ShouldBeRandomized || AutoSelectable)) { + Randstruct randstruct; + randstruct.reorganizeFields(*this,D); } if (isMsLayout(*this)) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index ea62b79570704..7edade7c758bf 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4411,6 +4411,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue(0)); } + if (Arg *A = Args.getLastArg(options::OPT_randstruct_auto)) { + CmdArgs.push_back( "-randstruct-auto" ); + } + // -fvisibility= and -fvisibility-ms-compat are of a piece. if (const Arg *A = Args.getLastArg(options::OPT_fvisibility_EQ, options::OPT_fvisibility_ms_compat)) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index cd08ac5847b3b..312c95d8b18b9 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1674,6 +1674,9 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, if (const Arg* A = Args.getLastArg(OPT_randstruct_seed)) { RandstructSeed = A->getValue(0); } + if (const Arg* A = Args.getLastArg(OPT_randstruct_auto)) { + RandstructAutoSelect = true; + } Opts.AddPluginActions = Args.getAllArgValues(OPT_add_plugin); for (const auto *AA : Args.filtered(OPT_plugin_arg)) Opts.PluginArgs[AA->getValue(0)].emplace_back(AA->getValue(1)); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index d1f0513da1219..60aa1fa3457e5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6856,6 +6856,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_RandomizeLayout: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_NoRandomizeLayout: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_CodeSeg: handleCodeSegAttr(S, D, AL); break; diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 8915de50f668d..5f16046c3d52e 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -80,6 +80,7 @@ // CHECK-NEXT: NoInstrumentFunction (SubjectMatchRule_function) // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function) // CHECK-NEXT: NoMips16 (SubjectMatchRule_function) +// CHECK-NEXT: NoRandomizeLayout (SubjectMatchRule_record) // CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global) // CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) // CHECK-NEXT: NoSpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)