Skip to content

Commit 04364fb

Browse files
authored
[randstruct] Also randomize composite function pointer structs (llvm#138385)
Check for struct members that are structs filled only with function pointers by recursively examining it. Since the lamba IsFunctionPointerOrForwardDecl cannot call itself directly, move it into a helper function, EntirelyFunctionPointers, so it can be called from the lambda. Add test for composite function pointer structs getting automatically randomized. Add more tests for validating automatic randomization vs explicitly annotated with "randomize_layout", and excluded with "no_randomize_layout". Reorder the "should we randomize?" "if" statement to check for enablement before checking for Record details. Fixes llvm#138355
1 parent 25fc52e commit 04364fb

File tree

3 files changed

+88
-31
lines changed

3 files changed

+88
-31
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6476,6 +6476,8 @@ class Sema final : public SemaBase {
64766476
void setupImplicitSpecialMemberType(CXXMethodDecl *SpecialMem,
64776477
QualType ResultTy,
64786478
ArrayRef<QualType> Args);
6479+
// Helper for ActOnFields to check for all function pointer members.
6480+
bool EntirelyFunctionPointers(const RecordDecl *Record);
64796481

64806482
// A cache representing if we've fully checked the various comparison category
64816483
// types stored in ASTContext. The bit-index corresponds to the integer value

clang/lib/Sema/SemaDecl.cpp

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19309,6 +19309,43 @@ static void ComputeSpecialMemberFunctionsEligiblity(Sema &S,
1930919309
CXXSpecialMemberKind::MoveAssignment);
1931019310
}
1931119311

19312+
bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) {
19313+
// Check to see if a FieldDecl is a pointer to a function.
19314+
auto IsFunctionPointerOrForwardDecl = [&](const Decl *D) {
19315+
const FieldDecl *FD = dyn_cast<FieldDecl>(D);
19316+
if (!FD) {
19317+
// Check whether this is a forward declaration that was inserted by
19318+
// Clang. This happens when a non-forward declared / defined type is
19319+
// used, e.g.:
19320+
//
19321+
// struct foo {
19322+
// struct bar *(*f)();
19323+
// struct bar *(*g)();
19324+
// };
19325+
//
19326+
// "struct bar" shows up in the decl AST as a "RecordDecl" with an
19327+
// incomplete definition.
19328+
if (const auto *TD = dyn_cast<TagDecl>(D))
19329+
return !TD->isCompleteDefinition();
19330+
return false;
19331+
}
19332+
QualType FieldType = FD->getType().getDesugaredType(Context);
19333+
if (isa<PointerType>(FieldType)) {
19334+
QualType PointeeType = cast<PointerType>(FieldType)->getPointeeType();
19335+
return PointeeType.getDesugaredType(Context)->isFunctionType();
19336+
}
19337+
// If a member is a struct entirely of function pointers, that counts too.
19338+
if (const RecordType *RT = FieldType->getAs<RecordType>()) {
19339+
const RecordDecl *Record = RT->getDecl();
19340+
if (Record->isStruct() && EntirelyFunctionPointers(Record))
19341+
return true;
19342+
}
19343+
return false;
19344+
};
19345+
19346+
return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl);
19347+
}
19348+
1931219349
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1931319350
ArrayRef<Decl *> Fields, SourceLocation LBrac,
1931419351
SourceLocation RBrac,
@@ -19646,41 +19683,13 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1964619683
// Handle attributes before checking the layout.
1964719684
ProcessDeclAttributeList(S, Record, Attrs);
1964819685

19649-
// Check to see if a FieldDecl is a pointer to a function.
19650-
auto IsFunctionPointerOrForwardDecl = [&](const Decl *D) {
19651-
const FieldDecl *FD = dyn_cast<FieldDecl>(D);
19652-
if (!FD) {
19653-
// Check whether this is a forward declaration that was inserted by
19654-
// Clang. This happens when a non-forward declared / defined type is
19655-
// used, e.g.:
19656-
//
19657-
// struct foo {
19658-
// struct bar *(*f)();
19659-
// struct bar *(*g)();
19660-
// };
19661-
//
19662-
// "struct bar" shows up in the decl AST as a "RecordDecl" with an
19663-
// incomplete definition.
19664-
if (const auto *TD = dyn_cast<TagDecl>(D))
19665-
return !TD->isCompleteDefinition();
19666-
return false;
19667-
}
19668-
QualType FieldType = FD->getType().getDesugaredType(Context);
19669-
if (isa<PointerType>(FieldType)) {
19670-
QualType PointeeType = cast<PointerType>(FieldType)->getPointeeType();
19671-
return PointeeType.getDesugaredType(Context)->isFunctionType();
19672-
}
19673-
return false;
19674-
};
19675-
1967619686
// Maybe randomize the record's decls. We automatically randomize a record
1967719687
// of function pointers, unless it has the "no_randomize_layout" attribute.
19678-
if (!getLangOpts().CPlusPlus &&
19688+
if (!getLangOpts().CPlusPlus && !getLangOpts().RandstructSeed.empty() &&
19689+
!Record->isRandomized() && !Record->isUnion() &&
1967919690
(Record->hasAttr<RandomizeLayoutAttr>() ||
1968019691
(!Record->hasAttr<NoRandomizeLayoutAttr>() &&
19681-
llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl))) &&
19682-
!Record->isUnion() && !getLangOpts().RandstructSeed.empty() &&
19683-
!Record->isRandomized()) {
19692+
EntirelyFunctionPointers(Record)))) {
1968419693
SmallVector<Decl *, 32> NewDeclOrdering;
1968519694
if (randstruct::randomizeStructureLayout(Context, Record,
1968619695
NewDeclOrdering))

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,49 @@ struct enum_decl_test {
7575
} __attribute__((randomize_layout));
7676

7777
struct enum_decl_test t13 = { BORK }; // Okay
78+
79+
struct mixed {
80+
int a;
81+
short b;
82+
unsigned c;
83+
char d;
84+
} __attribute__((randomize_layout));
85+
86+
struct mixed t14 = { 7 }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}
87+
struct mixed t15 = { .b = 8 }; // Okay
88+
89+
// This should be autodetected as randomized.
90+
struct funcs {
91+
func_ptr a;
92+
func_ptr b;
93+
func_ptr c;
94+
func_ptr d;
95+
};
96+
97+
struct funcs t16 = { .c = foo }; // Okay
98+
struct funcs t17 = { foo }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}
99+
100+
// This should be forced off.
101+
struct funcs_unshuffled {
102+
func_ptr a;
103+
func_ptr b;
104+
func_ptr c;
105+
func_ptr d;
106+
} __attribute__((no_randomize_layout));
107+
108+
struct funcs_unshuffled t18 = { .d = foo }; // Okay
109+
struct funcs_unshuffled t19 = { foo }; // Okay
110+
111+
// This is still all function pointers.
112+
// https://github.com/llvm/llvm-project/issues/138355
113+
struct funcs_composite {
114+
func_ptr a;
115+
func_ptr b;
116+
struct funcs inner;
117+
func_ptr c;
118+
func_ptr d;
119+
};
120+
121+
struct funcs_composite t20 = { .a = foo }; // Okay
122+
struct funcs_composite t21 = { .inner.c = foo }; // Okay
123+
struct funcs_composite t22 = { foo }; // expected-error {{a randomized struct can only be initialized with a designated initializer}}

0 commit comments

Comments
 (0)