Skip to content
Closed
1 change: 1 addition & 0 deletions clang/include/clang/AST/RandstructSeed.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
#include <string>
namespace clang {
extern std::string RandstructSeed;
extern bool RandstructAutoSelect;
}
#endif
3 changes: 3 additions & 0 deletions clang/include/clang/AST/RecordFieldReorganizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Decl *, 64> &NewOrder) const override;
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
32 changes: 30 additions & 2 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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));
}];
}
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,7 @@ def warn_unnecessary_packed : Warning<
"packed attribute is unnecessary for %0">, InGroup<Packed>, DefaultIgnore;
def warn_randomize_attr_union : Warning<
"union declared with 'randomize_layout' attribute">, InGroup<DiagGroup<"randomize-layout">>;
def warn_randomize_attr_conflict : Warning<
"struct declared with 'randomize_layout' and 'no_randomize_layout' attributes; "
"attribute 'no_randomize_layout' takes precedence">, InGroup<DiagGroup<"no-randomize-layout">>;
}
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,9 @@ def freroll_loops : Flag<["-"], "freroll-loops">, Group<f_Group>,
HelpText<"Turn on loop reroller">, Flags<[CC1Option]>;
def fno_reroll_loops : Flag<["-"], "fno-reroll-loops">, Group<f_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<f_Group>,
HelpText<"Process trigraph sequences">, Flags<[CC1Option]>;
def fno_trigraphs : Flag<["-"], "fno-trigraphs">, Group<f_Group>,
Expand Down
12 changes: 9 additions & 3 deletions clang/lib/AST/RecordFieldReorganizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

namespace clang {
std::string RandstructSeed = "";
bool RandstructAutoSelect = false;

void RecordFieldReorganizer::reorganizeFields(const ASTContext &C,
const RecordDecl *D) const {
Expand All @@ -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);

Expand All @@ -52,7 +52,6 @@ void RecordFieldReorganizer::reorganizeFields(const ASTContext &C,

commit(D, fields);
}

void RecordFieldReorganizer::commit(
const RecordDecl *D, SmallVectorImpl<Decl *> &NewFieldOrder) const {
Decl *First, *Last;
Expand Down Expand Up @@ -254,5 +253,12 @@ void Randstruct::reorganize(const ASTContext &C, const RecordDecl *D,
SmallVector<Decl *, 64> 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
25 changes: 15 additions & 10 deletions clang/lib/AST/RecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2987,18 +2987,23 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const {
if (Entry) return *Entry;

const ASTRecordLayout *NewEntry = nullptr;

bool ShouldBeRandomized = D->getAttr<RandomizeLayoutAttr>() != 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<NoRandomizeLayoutAttr>() != 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)) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6856,6 +6856,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_RandomizeLayout:
handleSimpleAttribute<RandomizeLayoutAttr>(S, D, AL);
break;
case ParsedAttr::AT_NoRandomizeLayout:
handleSimpleAttribute<NoRandomizeLayoutAttr>(S, D, AL);
break;
case ParsedAttr::AT_CodeSeg:
handleCodeSegAttr(S, D, AL);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down