xref: /llvm-project/clang/unittests/AST/RandstructTest.cpp (revision 6f79700830292d86afec5f3cf5143b00e6f3f1fd)
1 //===- unittest/AST/RandstructTest.cpp ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains tests for Clang's structure field layout randomization.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 /*
14  * Build this test suite by running `make ASTTests` in the build folder.
15  *
16  * Run this test suite by running the following in the build folder:
17  * ` ./tools/clang/unittests/AST/ASTTests
18  * --gtest_filter=RecordLayoutRandomization*`
19  */
20 
21 #include "clang/AST/Randstruct.h"
22 #include "gtest/gtest.h"
23 
24 #include "DeclMatcher.h"
25 #include "clang/AST/RecordLayout.h"
26 #include "clang/ASTMatchers/ASTMatchers.h"
27 #include "clang/Frontend/ASTUnit.h"
28 #include "clang/Testing/CommandLineArgs.h"
29 #include "clang/Tooling/Tooling.h"
30 #include "llvm/Support/ToolOutputFile.h"
31 
32 #include <vector>
33 
34 using namespace clang;
35 using namespace clang::ast_matchers;
36 using namespace clang::randstruct;
37 
38 using field_names = std::vector<std::string>;
39 
40 constexpr const char Seed[] = "1234567890abcdef";
41 
getRecordDeclFromAST(const ASTContext & C,const std::string & Name)42 static RecordDecl *getRecordDeclFromAST(const ASTContext &C,
43                                         const std::string &Name) {
44   RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match(
45       C.getTranslationUnitDecl(), recordDecl(hasName(Name)));
46   return RD;
47 }
48 
getFieldNamesFromRecord(const RecordDecl * RD)49 static std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) {
50   std::vector<std::string> Fields;
51 
52   Fields.reserve(8);
53   for (auto *Field : RD->fields())
54     Fields.push_back(Field->getNameAsString());
55 
56   return Fields;
57 }
58 
isSubsequence(const field_names & Seq,const field_names & Subseq)59 static bool isSubsequence(const field_names &Seq, const field_names &Subseq) {
60   unsigned SeqLen = Seq.size();
61   unsigned SubLen = Subseq.size();
62 
63   bool IsSubseq = false;
64   for (unsigned I = 0; I < SeqLen; ++I)
65     if (Seq[I] == Subseq[0]) {
66       IsSubseq = true;
67       for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) {
68         if (Seq[J + I] != Subseq[J]) {
69           IsSubseq = false;
70           break;
71         }
72       }
73     }
74 
75   return IsSubseq;
76 }
77 
recordsEqual(const std::unique_ptr<ASTUnit> & LHS,const std::unique_ptr<ASTUnit> & RHS,const std::string & RecordName)78 static bool recordsEqual(const std::unique_ptr<ASTUnit> &LHS,
79                          const std::unique_ptr<ASTUnit> &RHS,
80                          const std::string &RecordName) {
81   const RecordDecl *LHSRD =
82       getRecordDeclFromAST(LHS->getASTContext(), RecordName);
83   const RecordDecl *RHSRD =
84       getRecordDeclFromAST(LHS->getASTContext(), RecordName);
85 
86   return getFieldNamesFromRecord(LHSRD) == getFieldNamesFromRecord(RHSRD);
87 }
88 
89 static std::unique_ptr<ASTUnit>
makeAST(const std::string & SourceCode,bool ExpectError=false,std::vector<std::string> RecordNames=std::vector<std::string> ())90 makeAST(const std::string &SourceCode, bool ExpectError = false,
91         std::vector<std::string> RecordNames = std::vector<std::string>()) {
92   std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99);
93   Args.push_back("-frandomize-layout-seed=" + std::string(Seed));
94 
95   IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer();
96 
97   std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs(
98       SourceCode, Args, "input.c", "clang-tool",
99       std::make_shared<PCHContainerOperations>(),
100       tooling::getClangStripDependencyFileAdjuster(),
101       tooling::FileContentMappings(), &IgnoringConsumer);
102 
103   int SeedFileFD = -1;
104   llvm::SmallString<256> SeedFilename;
105   EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("seed", "rng", SeedFileFD,
106                                                   SeedFilename));
107   llvm::ToolOutputFile SeedFile(SeedFilename, SeedFileFD);
108   SeedFile.os() << Seed << "\n";
109 
110   Args.clear();
111   Args = getCommandLineArgsForTesting(Lang_C99);
112   Args.push_back("-frandomize-layout-seed-file=" +
113                  SeedFile.getFilename().str());
114 
115   std::unique_ptr<ASTUnit> ASTFileSeed = tooling::buildASTFromCodeWithArgs(
116       SourceCode, Args, "input.c", "clang-tool",
117       std::make_shared<PCHContainerOperations>(),
118       tooling::getClangStripDependencyFileAdjuster(),
119       tooling::FileContentMappings(), &IgnoringConsumer);
120 
121   if (!ExpectError) {
122     if (RecordNames.empty())
123       RecordNames.push_back("test");
124 
125     for (std::string Name : RecordNames)
126       EXPECT_TRUE(recordsEqual(AST, ASTFileSeed, Name));
127   }
128 
129   return AST;
130 }
131 
132 namespace clang {
133 namespace ast_matchers {
134 
declCount(const RecordDecl * RD)135 long declCount(const RecordDecl *RD) {
136   return llvm::count_if(RD->decls(), [&](const Decl *D) {
137     return isa<FieldDecl>(D) || isa<RecordDecl>(D);
138   });
139 }
140 
141 #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest
142 
TEST(RANDSTRUCT_TEST_SUITE_TEST,CanDetermineIfSubsequenceExists)143 TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) {
144   const field_names Seq = {"a", "b", "c", "d"};
145 
146   EXPECT_TRUE(isSubsequence(Seq, {"b", "c"}));
147   EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"}));
148   EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"}));
149   EXPECT_TRUE(isSubsequence(Seq, {"a"}));
150   EXPECT_FALSE(isSubsequence(Seq, {"a", "d"}));
151 }
152 
153 #define RANDSTRUCT_TEST RecordLayoutRandomization
154 
TEST(RANDSTRUCT_TEST,UnmarkedStruct)155 TEST(RANDSTRUCT_TEST, UnmarkedStruct) {
156   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
157     struct test {
158         int bacon;
159         long lettuce;
160         long long tomato;
161         float mayonnaise;
162     };
163   )c");
164 
165   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
166 
167   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
168   long OriginalDeclCount = declCount(RD);
169 
170   EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>());
171   EXPECT_FALSE(RD->isRandomized());
172   EXPECT_EQ(OriginalDeclCount, declCount(RD));
173 }
174 
TEST(RANDSTRUCT_TEST,MarkedNoRandomize)175 TEST(RANDSTRUCT_TEST, MarkedNoRandomize) {
176   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
177     struct test {
178         int bacon;
179         long lettuce;
180         long long tomato;
181         float mayonnaise;
182     } __attribute__((no_randomize_layout));
183   )c");
184 
185   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
186 
187   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
188   long OriginalDeclCount = declCount(RD);
189 
190   EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>());
191   EXPECT_FALSE(RD->isRandomized());
192   EXPECT_EQ(OriginalDeclCount, declCount(RD));
193 }
194 
TEST(RANDSTRUCT_TEST,MarkedRandomize)195 TEST(RANDSTRUCT_TEST, MarkedRandomize) {
196   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
197     struct test {
198         int bacon;
199         long lettuce;
200         long long tomato;
201         float mayonnaise;
202     } __attribute__((randomize_layout));
203   )c");
204 
205   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
206 
207   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
208   long OriginalDeclCount = declCount(RD);
209 
210   EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>());
211   EXPECT_TRUE(RD->isRandomized());
212   EXPECT_EQ(OriginalDeclCount, declCount(RD));
213 }
214 
TEST(RANDSTRUCT_TEST,MismatchedAttrsDeclVsDef)215 TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) {
216   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
217     struct test __attribute__((randomize_layout));
218     struct test {
219         int bacon;
220         long lettuce;
221         long long tomato;
222         float mayonnaise;
223     } __attribute__((no_randomize_layout));
224   )c",
225                                          true);
226 
227   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
228 
229   const DiagnosticsEngine &Diags = AST->getDiagnostics();
230 
231   EXPECT_FALSE(Diags.hasFatalErrorOccurred());
232   EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
233   EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
234   EXPECT_EQ(Diags.getNumWarnings(), 1u);
235   EXPECT_EQ(Diags.getNumErrors(), 0u);
236 }
237 
TEST(RANDSTRUCT_TEST,MismatchedAttrsRandomizeVsNoRandomize)238 TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) {
239   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
240     struct test {
241         int bacon;
242         long lettuce;
243         long long tomato;
244         float mayonnaise;
245     } __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
246   )c",
247                                          true);
248 
249   EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
250 
251   const DiagnosticsEngine &Diags = AST->getDiagnostics();
252 
253   EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
254   EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
255   EXPECT_EQ(Diags.getNumWarnings(), 0u);
256   EXPECT_EQ(Diags.getNumErrors(), 1u);
257 }
258 
TEST(RANDSTRUCT_TEST,MismatchedAttrsNoRandomizeVsRandomize)259 TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) {
260   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
261     struct test3 {
262         int bacon;
263         long lettuce;
264         long long tomato;
265         float mayonnaise;
266     } __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
267   )c",
268                                          true);
269 
270   EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
271 
272   const DiagnosticsEngine &Diags = AST->getDiagnostics();
273 
274   EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
275   EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
276   EXPECT_EQ(Diags.getNumWarnings(), 0u);
277   EXPECT_EQ(Diags.getNumErrors(), 1u);
278 }
279 
TEST(RANDSTRUCT_TEST,CheckAdjacentBitfieldsRemainAdjacentAfterRandomization)280 TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) {
281   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
282     struct test {
283         int a;
284         int b;
285         int x : 1;
286         int y : 1;
287         int z : 1;
288         int c;
289     } __attribute__((randomize_layout));
290   )c");
291 
292   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
293 
294   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
295   long OriginalDeclCount = declCount(RD);
296 
297   const field_names Actual = getFieldNamesFromRecord(RD);
298   const field_names Subseq = {"x", "y", "z"};
299 
300   EXPECT_TRUE(RD->isRandomized());
301   EXPECT_TRUE(isSubsequence(Actual, Subseq));
302   EXPECT_EQ(OriginalDeclCount, declCount(RD));
303 }
304 
TEST(RANDSTRUCT_TEST,CheckFlexibleArrayMemberRemainsAtEndOfStructure1)305 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) {
306   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
307     struct test {
308         int a;
309         int b;
310         int c;
311         int d;
312         int e;
313         int f;
314         int g;
315         int h;
316         char name[];
317     } __attribute__((randomize_layout));
318   )c");
319 
320   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
321 
322   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
323   long OriginalDeclCount = declCount(RD);
324 
325   EXPECT_TRUE(RD->hasFlexibleArrayMember());
326   EXPECT_TRUE(RD->isRandomized());
327   EXPECT_EQ(OriginalDeclCount, declCount(RD));
328   EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
329 }
330 
TEST(RANDSTRUCT_TEST,CheckFlexibleArrayMemberRemainsAtEndOfStructure2)331 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure2) {
332   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
333     struct test {
334         int a;
335         int b;
336         int c;
337         int d;
338         int e;
339         int f;
340         int g;
341         int h;
342         char name[0];
343     } __attribute__((randomize_layout));
344   )c");
345 
346   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
347 
348   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
349   long OriginalDeclCount = declCount(RD);
350 
351   EXPECT_FALSE(RD->hasFlexibleArrayMember());
352   EXPECT_TRUE(RD->isRandomized());
353   EXPECT_EQ(OriginalDeclCount, declCount(RD));
354   EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
355 }
356 
TEST(RANDSTRUCT_TEST,CheckFlexibleArrayMemberRemainsAtEndOfStructure3)357 TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure3) {
358   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
359     struct test {
360         int a;
361         int b;
362         int c;
363         int d;
364         int e;
365         int f;
366         int g;
367         int h;
368         char name[1];
369     } __attribute__((randomize_layout));
370   )c");
371 
372   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
373 
374   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
375   long OriginalDeclCount = declCount(RD);
376 
377   EXPECT_FALSE(RD->hasFlexibleArrayMember());
378   EXPECT_TRUE(RD->isRandomized());
379   EXPECT_EQ(OriginalDeclCount, declCount(RD));
380   EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name");
381 }
382 
TEST(RANDSTRUCT_TEST,RandstructDoesNotOverrideThePackedAttr)383 TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) {
384   std::unique_ptr<ASTUnit> AST =
385       makeAST(R"c(
386     struct test_struct {
387         char a;
388         float b[3];
389         short c;
390         int d;
391     } __attribute__((packed, randomize_layout));
392 
393     struct another_struct {
394         char a;
395         char b[5];
396         int c;
397     } __attribute__((packed, randomize_layout));
398 
399     struct last_struct {
400         char a;
401         long long b;
402         int c[];
403     } __attribute__((packed, randomize_layout));
404   )c",
405               false,
406               std::vector<std::string>(
407                   {"test_struct", "another_struct", "last_struct"}));
408 
409   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
410 
411   // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
412   // Clang's RecordBuilders can actually flesh out the information like
413   // alignment, etc.
414   {
415     const RecordDecl *RD =
416         getRecordDeclFromAST(AST->getASTContext(), "test_struct");
417     const ASTRecordLayout *Layout =
418         &AST->getASTContext().getASTRecordLayout(RD);
419     long OriginalDeclCount = declCount(RD);
420 
421     EXPECT_TRUE(RD->isRandomized());
422     EXPECT_EQ(19, Layout->getSize().getQuantity());
423     EXPECT_EQ(OriginalDeclCount, declCount(RD));
424   }
425 
426   {
427     const RecordDecl *RD =
428         getRecordDeclFromAST(AST->getASTContext(), "another_struct");
429     const ASTRecordLayout *Layout =
430         &AST->getASTContext().getASTRecordLayout(RD);
431     long OriginalDeclCount = declCount(RD);
432 
433     EXPECT_TRUE(RD->isRandomized());
434     EXPECT_EQ(10, Layout->getSize().getQuantity());
435     EXPECT_EQ(OriginalDeclCount, declCount(RD));
436   }
437 
438   {
439     const RecordDecl *RD =
440         getRecordDeclFromAST(AST->getASTContext(), "last_struct");
441     const ASTRecordLayout *Layout =
442         &AST->getASTContext().getASTRecordLayout(RD);
443     long OriginalDeclCount = declCount(RD);
444 
445     EXPECT_TRUE(RD->isRandomized());
446     EXPECT_EQ(9, Layout->getSize().getQuantity());
447     EXPECT_EQ(OriginalDeclCount, declCount(RD));
448   }
449 }
450 
TEST(RANDSTRUCT_TEST,ZeroWidthBitfieldsSeparateAllocationUnits)451 TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) {
452   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
453     struct test {
454         int a : 1;
455         int   : 0;
456         int b : 1;
457     } __attribute__((randomize_layout));
458   )c");
459 
460   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
461 
462   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
463   long OriginalDeclCount = declCount(RD);
464 
465   EXPECT_TRUE(RD->isRandomized());
466   EXPECT_EQ(OriginalDeclCount, declCount(RD));
467 }
468 
TEST(RANDSTRUCT_TEST,RandstructDoesNotRandomizeUnionFieldOrder)469 TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) {
470   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
471     union test {
472         int a;
473         int b;
474         int c;
475         int d;
476         int e;
477         int f;
478     } __attribute__((randomize_layout));
479   )c");
480 
481   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
482 
483   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
484   long OriginalDeclCount = declCount(RD);
485 
486   EXPECT_FALSE(RD->isRandomized());
487   EXPECT_EQ(OriginalDeclCount, declCount(RD));
488 }
489 
TEST(RANDSTRUCT_TEST,AnonymousStructsAndUnionsRetainFieldOrder)490 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
491   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
492     struct test {
493         int a;
494         struct sub_struct {
495             int b;
496             int c;
497             int d;
498             int e;
499             int f;
500         } __attribute__((randomize_layout)) s;
501         int f;
502         struct {
503             int g;
504             int h;
505             int i;
506             int j;
507             int k;
508         };
509         int l;
510         union {
511             int m;
512             int n;
513             int o;
514             int p;
515             int q;
516         };
517         int r;
518     } __attribute__((randomize_layout));
519   )c");
520 
521   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
522 
523   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
524   long OriginalDeclCount = declCount(RD);
525 
526   EXPECT_TRUE(RD->isRandomized());
527   EXPECT_EQ(OriginalDeclCount, declCount(RD));
528 
529   bool AnonStructTested = false;
530   bool AnonUnionTested = false;
531 
532   for (const Decl *D : RD->decls())
533     if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
534       if (const auto *Record = FD->getType()->getAs<RecordType>()) {
535         RD = Record->getDecl();
536         if (RD->isAnonymousStructOrUnion()) {
537           // These field orders shouldn't change.
538           if (RD->isUnion()) {
539             const field_names Expected = {"m", "n", "o", "p", "q"};
540 
541             EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
542             AnonUnionTested = true;
543           } else {
544             const field_names Expected = {"g", "h", "i", "j", "k"};
545 
546             EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
547             AnonStructTested = true;
548           }
549         }
550       }
551     }
552 
553   EXPECT_TRUE(AnonStructTested);
554   EXPECT_TRUE(AnonUnionTested);
555 }
556 
TEST(RANDSTRUCT_TEST,AnonymousStructsAndUnionsReferenced)557 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) {
558   std::unique_ptr<ASTUnit> AST = makeAST(R"c(
559     struct test {
560         int bacon;
561         long lettuce;
562         struct { double avocado; char blech; };
563         long long tomato;
564         union { char toast[8]; unsigned toast_thing; };
565         float mayonnaise;
566     } __attribute__((randomize_layout));
567 
568     int foo(struct test *t) {
569       return t->blech;
570     }
571 
572     char *bar(struct test *t) {
573       return t->toast;
574     }
575   )c");
576 
577   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
578 
579   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
580   long OriginalDeclCount = declCount(RD);
581 
582   EXPECT_TRUE(RD->isRandomized());
583   EXPECT_EQ(OriginalDeclCount, declCount(RD));
584 }
585 
TEST(RANDSTRUCT_TEST,AutoRandomizeStructOfFunctionPointers)586 TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) {
587   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
588     typedef void (*func_ptr)();
589 
590     struct test {
591       func_ptr a;
592       func_ptr b;
593       func_ptr c;
594       func_ptr d;
595       func_ptr e;
596       func_ptr f;
597       func_ptr g;
598     };
599   )c");
600 
601   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
602 
603   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
604 
605   EXPECT_TRUE(RD->isRandomized());
606 }
607 
TEST(RANDSTRUCT_TEST,DisableAutoRandomizeStructOfFunctionPointers)608 TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) {
609   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
610     typedef void (*func_ptr)();
611 
612     struct test {
613       func_ptr a;
614       func_ptr b;
615       func_ptr c;
616       func_ptr d;
617       func_ptr e;
618       func_ptr f;
619       func_ptr g;
620     } __attribute__((no_randomize_layout));
621   )c");
622 
623   EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
624 
625   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
626 
627   EXPECT_FALSE(RD->isRandomized());
628 }
629 
630 } // namespace ast_matchers
631 } // namespace clang
632