Skip to content

Commit 9c06ecc

Browse files
committed
[randstruct] Also randomize composite function pointer structs
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 #138355
1 parent f46ff4c commit 9c06ecc

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
@@ -6280,6 +6280,8 @@ class Sema final : public SemaBase {
62806280
void setupImplicitSpecialMemberType(CXXMethodDecl *SpecialMem,
62816281
QualType ResultTy,
62826282
ArrayRef<QualType> Args);
6283+
// Helper for ActOnFields to check for all function pointer members.
6284+
bool EntirelyFunctionPointers(const RecordDecl *Record);
62836285

62846286
// A cache representing if we've fully checked the various comparison category
62856287
// 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
@@ -19270,6 +19270,43 @@ static void ComputeSpecialMemberFunctionsEligiblity(Sema &S,
1927019270
CXXSpecialMemberKind::MoveAssignment);
1927119271
}
1927219272

19273+
bool Sema::EntirelyFunctionPointers(const RecordDecl *Record) {
19274+
// Check to see if a FieldDecl is a pointer to a function.
19275+
auto IsFunctionPointerOrForwardDecl = [&](const Decl *D) {
19276+
const FieldDecl *FD = dyn_cast<FieldDecl>(D);
19277+
if (!FD) {
19278+
// Check whether this is a forward declaration that was inserted by
19279+
// Clang. This happens when a non-forward declared / defined type is
19280+
// used, e.g.:
19281+
//
19282+
// struct foo {
19283+
// struct bar *(*f)();
19284+
// struct bar *(*g)();
19285+
// };
19286+
//
19287+
// "struct bar" shows up in the decl AST as a "RecordDecl" with an
19288+
// incomplete definition.
19289+
if (const auto *TD = dyn_cast<TagDecl>(D))
19290+
return !TD->isCompleteDefinition();
19291+
return false;
19292+
}
19293+
QualType FieldType = FD->getType().getDesugaredType(Context);
19294+
if (isa<PointerType>(FieldType)) {
19295+
QualType PointeeType = cast<PointerType>(FieldType)->getPointeeType();
19296+
return PointeeType.getDesugaredType(Context)->isFunctionType();
19297+
}
19298+
// If a member is a struct entirely of function pointers, that counts too.
19299+
if (const RecordType *RT = FieldType->getAs<RecordType>()) {
19300+
const RecordDecl *Record = RT->getDecl();
19301+
if (Record->isStruct() && EntirelyFunctionPointers(Record))
19302+
return true;
19303+
}
19304+
return false;
19305+
};
19306+
19307+
return llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl);
19308+
}
19309+
1927319310
void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1927419311
ArrayRef<Decl *> Fields, SourceLocation LBrac,
1927519312
SourceLocation RBrac,
@@ -19607,41 +19644,13 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
1960719644
// Handle attributes before checking the layout.
1960819645
ProcessDeclAttributeList(S, Record, Attrs);
1960919646

19610-
// Check to see if a FieldDecl is a pointer to a function.
19611-
auto IsFunctionPointerOrForwardDecl = [&](const Decl *D) {
19612-
const FieldDecl *FD = dyn_cast<FieldDecl>(D);
19613-
if (!FD) {
19614-
// Check whether this is a forward declaration that was inserted by
19615-
// Clang. This happens when a non-forward declared / defined type is
19616-
// used, e.g.:
19617-
//
19618-
// struct foo {
19619-
// struct bar *(*f)();
19620-
// struct bar *(*g)();
19621-
// };
19622-
//
19623-
// "struct bar" shows up in the decl AST as a "RecordDecl" with an
19624-
// incomplete definition.
19625-
if (const auto *TD = dyn_cast<TagDecl>(D))
19626-
return !TD->isCompleteDefinition();
19627-
return false;
19628-
}
19629-
QualType FieldType = FD->getType().getDesugaredType(Context);
19630-
if (isa<PointerType>(FieldType)) {
19631-
QualType PointeeType = cast<PointerType>(FieldType)->getPointeeType();
19632-
return PointeeType.getDesugaredType(Context)->isFunctionType();
19633-
}
19634-
return false;
19635-
};
19636-
1963719647
// Maybe randomize the record's decls. We automatically randomize a record
1963819648
// of function pointers, unless it has the "no_randomize_layout" attribute.
19639-
if (!getLangOpts().CPlusPlus &&
19649+
if (!getLangOpts().CPlusPlus && !getLangOpts().RandstructSeed.empty() &&
19650+
!Record->isRandomized() && !Record->isUnion() &&
1964019651
(Record->hasAttr<RandomizeLayoutAttr>() ||
1964119652
(!Record->hasAttr<NoRandomizeLayoutAttr>() &&
19642-
llvm::all_of(Record->decls(), IsFunctionPointerOrForwardDecl))) &&
19643-
!Record->isUnion() && !getLangOpts().RandstructSeed.empty() &&
19644-
!Record->isRandomized()) {
19653+
EntirelyFunctionPointers(Record)))) {
1964519654
SmallVector<Decl *, 32> NewDeclOrdering;
1964619655
if (randstruct::randomizeStructureLayout(Context, Record,
1964719656
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)