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 | |
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 | |
49 | static std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) { |
50 | std::vector<std::string> Fields; |
51 | |
52 | Fields.reserve(n: 8); |
53 | for (auto *Field : RD->fields()) |
54 | Fields.push_back(Field->getNameAsString()); |
55 | |
56 | return Fields; |
57 | } |
58 | |
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 | |
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(C: LHS->getASTContext(), Name: RecordName); |
83 | const RecordDecl *RHSRD = |
84 | getRecordDeclFromAST(C: LHS->getASTContext(), Name: RecordName); |
85 | |
86 | return getFieldNamesFromRecord(RD: LHSRD) == getFieldNamesFromRecord(RD: RHSRD); |
87 | } |
88 | |
89 | static std::unique_ptr<ASTUnit> |
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: Lang_C99); |
93 | Args.push_back(x: "-frandomize-layout-seed=" + std::string(Seed)); |
94 | |
95 | IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer(); |
96 | |
97 | std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs( |
98 | Code: SourceCode, Args, FileName: "input.c" , ToolName: "clang-tool" , |
99 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
100 | Adjuster: tooling::getClangStripDependencyFileAdjuster(), |
101 | VirtualMappedFiles: tooling::FileContentMappings(), DiagConsumer: &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: Lang_C99); |
112 | Args.push_back(x: "-frandomize-layout-seed-file=" + |
113 | SeedFile.getFilename().str()); |
114 | |
115 | std::unique_ptr<ASTUnit> ASTFileSeed = tooling::buildASTFromCodeWithArgs( |
116 | Code: SourceCode, Args, FileName: "input.c" , ToolName: "clang-tool" , |
117 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
118 | Adjuster: tooling::getClangStripDependencyFileAdjuster(), |
119 | VirtualMappedFiles: tooling::FileContentMappings(), DiagConsumer: &IgnoringConsumer); |
120 | |
121 | if (!ExpectError) { |
122 | if (RecordNames.empty()) |
123 | RecordNames.push_back(x: "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 | |
135 | long declCount(const RecordDecl *RD) { |
136 | return llvm::count_if(RD->decls(), [&](const Decl *D) { |
137 | return isa<FieldDecl>(Val: D) || isa<RecordDecl>(Val: D); |
138 | }); |
139 | } |
140 | |
141 | #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest |
142 | |
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 | |
155 | TEST(RANDSTRUCT_TEST, UnmarkedStruct) { |
156 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
175 | TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { |
176 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
195 | TEST(RANDSTRUCT_TEST, MarkedRandomize) { |
196 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
215 | TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) { |
216 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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 | ExpectError: 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 | |
238 | TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) { |
239 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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 | ExpectError: 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 | |
259 | TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) { |
260 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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 | ExpectError: 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 | |
280 | TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) { |
281 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
305 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) { |
306 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
331 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure2) { |
332 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
357 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure3) { |
358 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
383 | TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { |
384 | std::unique_ptr<ASTUnit> AST = |
385 | makeAST(SourceCode: 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 | ExpectError: false, |
406 | RecordNames: 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(C: AST->getASTContext(), Name: "test_struct" ); |
417 | const ASTRecordLayout *Layout = |
418 | &AST->getASTContext().getASTRecordLayout(D: 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(C: AST->getASTContext(), Name: "another_struct" ); |
429 | const ASTRecordLayout *Layout = |
430 | &AST->getASTContext().getASTRecordLayout(D: 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(C: AST->getASTContext(), Name: "last_struct" ); |
441 | const ASTRecordLayout *Layout = |
442 | &AST->getASTContext().getASTRecordLayout(D: 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 | |
451 | TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) { |
452 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "test" ); |
463 | long OriginalDeclCount = declCount(RD); |
464 | |
465 | EXPECT_TRUE(RD->isRandomized()); |
466 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
467 | } |
468 | |
469 | TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) { |
470 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "test" ); |
484 | long OriginalDeclCount = declCount(RD); |
485 | |
486 | EXPECT_FALSE(RD->isRandomized()); |
487 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
488 | } |
489 | |
490 | TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) { |
491 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "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 | |
557 | TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) { |
558 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "test" ); |
580 | long OriginalDeclCount = declCount(RD); |
581 | |
582 | EXPECT_TRUE(RD->isRandomized()); |
583 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
584 | } |
585 | |
586 | TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) { |
587 | const std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "test" ); |
604 | |
605 | EXPECT_TRUE(RD->isRandomized()); |
606 | } |
607 | |
608 | TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) { |
609 | const std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: 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(C: AST->getASTContext(), Name: "test" ); |
626 | |
627 | EXPECT_FALSE(RD->isRandomized()); |
628 | } |
629 | |
630 | } // namespace ast_matchers |
631 | } // namespace clang |
632 | |