1 //===- unittest/AST/RandstructTest.cpp ------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file contains tests for Clang's structure field layout randomization.
11 //===----------------------------------------------------------------------===//
14 * Build this test suite by running `make ASTTests` in the build folder.
16 * Run this test suite by running the following in the build folder:
17 * ` ./tools/clang/unittests/AST/ASTTests
18 * --gtest_filter=RecordLayoutRandomization*`
21 #include "clang/AST/Randstruct.h"
22 #include "gtest/gtest.h"
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"
34 using namespace clang
;
35 using namespace clang::ast_matchers
;
36 using namespace clang::randstruct
;
38 using field_names
= std::vector
<std::string
>;
40 constexpr const char Seed
[] = "1234567890abcdef";
42 static RecordDecl
*getRecordDeclFromAST(const ASTContext
&C
,
43 const std::string
&Name
) {
44 RecordDecl
*RD
= FirstDeclMatcher
<RecordDecl
>().match(
45 C
.getTranslationUnitDecl(), recordDecl(hasName(Name
)));
49 static std::vector
<std::string
> getFieldNamesFromRecord(const RecordDecl
*RD
) {
50 std::vector
<std::string
> Fields
;
53 for (auto *Field
: RD
->fields())
54 Fields
.push_back(Field
->getNameAsString());
59 static bool isSubsequence(const field_names
&Seq
, const field_names
&Subseq
) {
60 unsigned SeqLen
= Seq
.size();
61 unsigned SubLen
= Subseq
.size();
63 bool IsSubseq
= false;
64 for (unsigned I
= 0; I
< SeqLen
; ++I
)
65 if (Seq
[I
] == Subseq
[0]) {
67 for (unsigned J
= 0; J
+ I
< SeqLen
&& J
< SubLen
; ++J
) {
68 if (Seq
[J
+ I
] != Subseq
[J
]) {
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
);
86 return getFieldNamesFromRecord(LHSRD
) == getFieldNamesFromRecord(RHSRD
);
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_C99
);
93 Args
.push_back("-frandomize-layout-seed=" + std::string(Seed
));
95 IgnoringDiagConsumer IgnoringConsumer
= IgnoringDiagConsumer();
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
);
104 llvm::SmallString
<256> SeedFilename
;
105 EXPECT_FALSE(llvm::sys::fs::createTemporaryFile("seed", "rng", SeedFileFD
,
107 llvm::ToolOutputFile
SeedFile(SeedFilename
, SeedFileFD
);
108 SeedFile
.os() << Seed
<< "\n";
111 Args
= getCommandLineArgsForTesting(Lang_C99
);
112 Args
.push_back("-frandomize-layout-seed-file=" +
113 SeedFile
.getFilename().str());
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
);
122 if (RecordNames
.empty())
123 RecordNames
.push_back("test");
125 for (std::string Name
: RecordNames
)
126 EXPECT_TRUE(recordsEqual(AST
, ASTFileSeed
, Name
));
133 namespace ast_matchers
{
135 long declCount(const RecordDecl
*RD
) {
136 return llvm::count_if(RD
->decls(), [&](const Decl
*D
) {
137 return isa
<FieldDecl
>(D
) || isa
<RecordDecl
>(D
);
141 #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest
143 TEST(RANDSTRUCT_TEST_SUITE_TEST
, CanDetermineIfSubsequenceExists
) {
144 const field_names Seq
= {"a", "b", "c", "d"};
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"}));
153 #define RANDSTRUCT_TEST RecordLayoutRandomization
155 TEST(RANDSTRUCT_TEST
, UnmarkedStruct
) {
156 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
165 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
167 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
168 long OriginalDeclCount
= declCount(RD
);
170 EXPECT_FALSE(RD
->hasAttr
<RandomizeLayoutAttr
>());
171 EXPECT_FALSE(RD
->isRandomized());
172 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
175 TEST(RANDSTRUCT_TEST
, MarkedNoRandomize
) {
176 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
182 } __attribute__((no_randomize_layout));
185 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
187 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
188 long OriginalDeclCount
= declCount(RD
);
190 EXPECT_TRUE(RD
->hasAttr
<NoRandomizeLayoutAttr
>());
191 EXPECT_FALSE(RD
->isRandomized());
192 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
195 TEST(RANDSTRUCT_TEST
, MarkedRandomize
) {
196 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
202 } __attribute__((randomize_layout));
205 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
207 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
208 long OriginalDeclCount
= declCount(RD
);
210 EXPECT_TRUE(RD
->hasAttr
<RandomizeLayoutAttr
>());
211 EXPECT_TRUE(RD
->isRandomized());
212 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
215 TEST(RANDSTRUCT_TEST
, MismatchedAttrsDeclVsDef
) {
216 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
217 struct test __attribute__((randomize_layout));
223 } __attribute__((no_randomize_layout));
227 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
229 const DiagnosticsEngine
&Diags
= AST
->getDiagnostics();
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);
238 TEST(RANDSTRUCT_TEST
, MismatchedAttrsRandomizeVsNoRandomize
) {
239 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
245 } __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
249 EXPECT_TRUE(AST
->getDiagnostics().hasErrorOccurred());
251 const DiagnosticsEngine
&Diags
= AST
->getDiagnostics();
253 EXPECT_TRUE(Diags
.hasUncompilableErrorOccurred());
254 EXPECT_TRUE(Diags
.hasUnrecoverableErrorOccurred());
255 EXPECT_EQ(Diags
.getNumWarnings(), 0u);
256 EXPECT_EQ(Diags
.getNumErrors(), 1u);
259 TEST(RANDSTRUCT_TEST
, MismatchedAttrsNoRandomizeVsRandomize
) {
260 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
266 } __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
270 EXPECT_TRUE(AST
->getDiagnostics().hasErrorOccurred());
272 const DiagnosticsEngine
&Diags
= AST
->getDiagnostics();
274 EXPECT_TRUE(Diags
.hasUncompilableErrorOccurred());
275 EXPECT_TRUE(Diags
.hasUnrecoverableErrorOccurred());
276 EXPECT_EQ(Diags
.getNumWarnings(), 0u);
277 EXPECT_EQ(Diags
.getNumErrors(), 1u);
280 TEST(RANDSTRUCT_TEST
, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization
) {
281 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
289 } __attribute__((randomize_layout));
292 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
294 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
295 long OriginalDeclCount
= declCount(RD
);
297 const field_names Actual
= getFieldNamesFromRecord(RD
);
298 const field_names Subseq
= {"x", "y", "z"};
300 EXPECT_TRUE(RD
->isRandomized());
301 EXPECT_TRUE(isSubsequence(Actual
, Subseq
));
302 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
305 TEST(RANDSTRUCT_TEST
, CheckFlexibleArrayMemberRemainsAtEndOfStructure1
) {
306 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
317 } __attribute__((randomize_layout));
320 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
322 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
323 long OriginalDeclCount
= declCount(RD
);
325 EXPECT_TRUE(RD
->hasFlexibleArrayMember());
326 EXPECT_TRUE(RD
->isRandomized());
327 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
328 EXPECT_EQ(getFieldNamesFromRecord(RD
).back(), "name");
331 TEST(RANDSTRUCT_TEST
, CheckFlexibleArrayMemberRemainsAtEndOfStructure2
) {
332 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
343 } __attribute__((randomize_layout));
346 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
348 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
349 long OriginalDeclCount
= declCount(RD
);
351 EXPECT_FALSE(RD
->hasFlexibleArrayMember());
352 EXPECT_TRUE(RD
->isRandomized());
353 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
354 EXPECT_EQ(getFieldNamesFromRecord(RD
).back(), "name");
357 TEST(RANDSTRUCT_TEST
, CheckFlexibleArrayMemberRemainsAtEndOfStructure3
) {
358 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
369 } __attribute__((randomize_layout));
372 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
374 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
375 long OriginalDeclCount
= declCount(RD
);
377 EXPECT_FALSE(RD
->hasFlexibleArrayMember());
378 EXPECT_TRUE(RD
->isRandomized());
379 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
380 EXPECT_EQ(getFieldNamesFromRecord(RD
).back(), "name");
383 TEST(RANDSTRUCT_TEST
, RandstructDoesNotOverrideThePackedAttr
) {
384 std::unique_ptr
<ASTUnit
> AST
=
391 } __attribute__((packed, randomize_layout));
393 struct another_struct {
397 } __attribute__((packed, randomize_layout));
403 } __attribute__((packed, randomize_layout));
406 std::vector
<std::string
>(
407 {"test_struct", "another_struct", "last_struct"}));
409 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
411 // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
412 // Clang's RecordBuilders can actually flesh out the information like
415 const RecordDecl
*RD
=
416 getRecordDeclFromAST(AST
->getASTContext(), "test_struct");
417 const ASTRecordLayout
*Layout
=
418 &AST
->getASTContext().getASTRecordLayout(RD
);
419 long OriginalDeclCount
= declCount(RD
);
421 EXPECT_TRUE(RD
->isRandomized());
422 EXPECT_EQ(19, Layout
->getSize().getQuantity());
423 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
427 const RecordDecl
*RD
=
428 getRecordDeclFromAST(AST
->getASTContext(), "another_struct");
429 const ASTRecordLayout
*Layout
=
430 &AST
->getASTContext().getASTRecordLayout(RD
);
431 long OriginalDeclCount
= declCount(RD
);
433 EXPECT_TRUE(RD
->isRandomized());
434 EXPECT_EQ(10, Layout
->getSize().getQuantity());
435 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
439 const RecordDecl
*RD
=
440 getRecordDeclFromAST(AST
->getASTContext(), "last_struct");
441 const ASTRecordLayout
*Layout
=
442 &AST
->getASTContext().getASTRecordLayout(RD
);
443 long OriginalDeclCount
= declCount(RD
);
445 EXPECT_TRUE(RD
->isRandomized());
446 EXPECT_EQ(9, Layout
->getSize().getQuantity());
447 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
451 TEST(RANDSTRUCT_TEST
, ZeroWidthBitfieldsSeparateAllocationUnits
) {
452 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
457 } __attribute__((randomize_layout));
460 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
462 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
463 long OriginalDeclCount
= declCount(RD
);
465 EXPECT_TRUE(RD
->isRandomized());
466 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
469 TEST(RANDSTRUCT_TEST
, RandstructDoesNotRandomizeUnionFieldOrder
) {
470 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
478 } __attribute__((randomize_layout));
481 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
483 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
484 long OriginalDeclCount
= declCount(RD
);
486 EXPECT_FALSE(RD
->isRandomized());
487 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
490 TEST(RANDSTRUCT_TEST
, AnonymousStructsAndUnionsRetainFieldOrder
) {
491 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
500 } __attribute__((randomize_layout)) s;
518 } __attribute__((randomize_layout));
521 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
523 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
524 long OriginalDeclCount
= declCount(RD
);
526 EXPECT_TRUE(RD
->isRandomized());
527 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
529 bool AnonStructTested
= false;
530 bool AnonUnionTested
= false;
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.
539 const field_names Expected
= {"m", "n", "o", "p", "q"};
541 EXPECT_EQ(Expected
, getFieldNamesFromRecord(RD
));
542 AnonUnionTested
= true;
544 const field_names Expected
= {"g", "h", "i", "j", "k"};
546 EXPECT_EQ(Expected
, getFieldNamesFromRecord(RD
));
547 AnonStructTested
= true;
553 EXPECT_TRUE(AnonStructTested
);
554 EXPECT_TRUE(AnonUnionTested
);
557 TEST(RANDSTRUCT_TEST
, AnonymousStructsAndUnionsReferenced
) {
558 std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
562 struct { double avocado; char blech; };
564 union { char toast[8]; unsigned toast_thing; };
566 } __attribute__((randomize_layout));
568 int foo(struct test *t) {
572 char *bar(struct test *t) {
577 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
579 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
580 long OriginalDeclCount
= declCount(RD
);
582 EXPECT_TRUE(RD
->isRandomized());
583 EXPECT_EQ(OriginalDeclCount
, declCount(RD
));
586 TEST(RANDSTRUCT_TEST
, AutoRandomizeStructOfFunctionPointers
) {
587 const std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
588 typedef void (*func_ptr)();
601 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
603 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
605 EXPECT_TRUE(RD
->isRandomized());
608 TEST(RANDSTRUCT_TEST
, DisableAutoRandomizeStructOfFunctionPointers
) {
609 const std::unique_ptr
<ASTUnit
> AST
= makeAST(R
"c(
610 typedef void (*func_ptr)();
620 } __attribute__((no_randomize_layout));
623 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
625 const RecordDecl
*RD
= getRecordDeclFromAST(AST
->getASTContext(), "test");
627 EXPECT_FALSE(RD
->isRandomized());
630 } // namespace ast_matchers