xref: /llvm-project/clang/unittests/AST/RandstructTest.cpp (revision 3f0587d0c668202bb89d29a25432aa290e551a31)
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=StructureLayoutRandomization*`
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 
31 #include <vector>
32 
33 using namespace clang;
34 using namespace clang::ast_matchers;
35 using namespace clang::randstruct;
36 
37 using field_names = std::vector<std::string>;
38 
39 namespace {
40 
41 std::unique_ptr<ASTUnit> makeAST(const std::string &SourceCode,
42                                  bool ExpectErr = false) {
43   std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99);
44   Args.push_back("-frandomize-layout-seed=1234567890abcdef");
45 
46   IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer();
47 
48   std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs(
49       SourceCode, Args, "input.c", "clang-tool",
50       std::make_shared<PCHContainerOperations>(),
51       tooling::getClangStripDependencyFileAdjuster(),
52       tooling::FileContentMappings(), &IgnoringConsumer);
53 
54   if (ExpectErr)
55     EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
56   else
57     EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
58 
59   return AST;
60 }
61 
62 RecordDecl *getRecordDeclFromAST(const ASTContext &C, const std::string &Name) {
63   RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match(
64       C.getTranslationUnitDecl(), recordDecl(hasName(Name)));
65   return RD;
66 }
67 
68 std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) {
69   std::vector<std::string> Fields;
70 
71   Fields.reserve(8);
72   for (auto *Field : RD->fields())
73     Fields.push_back(Field->getNameAsString());
74 
75   return Fields;
76 }
77 
78 bool isSubsequence(const field_names &Seq, const field_names &Subseq) {
79   unsigned SeqLen = Seq.size();
80   unsigned SubLen = Subseq.size();
81 
82   bool IsSubseq = false;
83   for (unsigned I = 0; I < SeqLen; ++I)
84     if (Seq[I] == Subseq[0]) {
85       IsSubseq = true;
86       for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) {
87         if (Seq[J + I] != Subseq[J]) {
88           IsSubseq = false;
89           break;
90         }
91       }
92     }
93 
94   return IsSubseq;
95 }
96 
97 } // end anonymous namespace
98 
99 namespace clang {
100 namespace ast_matchers {
101 
102 #define RANDSTRUCT_TEST_SUITE_TEST StructureLayoutRandomizationTestSuiteTest
103 
104 TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) {
105   const field_names Seq = {"a", "b", "c", "d"};
106 
107   ASSERT_TRUE(isSubsequence(Seq, {"b", "c"}));
108   ASSERT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"}));
109   ASSERT_TRUE(isSubsequence(Seq, {"b", "c", "d"}));
110   ASSERT_TRUE(isSubsequence(Seq, {"a"}));
111   ASSERT_FALSE(isSubsequence(Seq, {"a", "d"}));
112 }
113 
114 #define RANDSTRUCT_TEST StructureLayoutRandomization
115 
116 TEST(RANDSTRUCT_TEST, UnmarkedStruct) {
117   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
118     struct test {
119         int bacon;
120         long lettuce;
121         long long tomato;
122         float mayonnaise;
123     };
124   )c");
125 
126   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
127   const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
128 
129   ASSERT_FALSE(RD->hasAttr<RandomizeLayoutAttr>());
130   ASSERT_FALSE(RD->isRandomized());
131   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
132 }
133 
134 TEST(RANDSTRUCT_TEST, MarkedNoRandomize) {
135   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
136     struct test {
137         int bacon;
138         long lettuce;
139         long long tomato;
140         float mayonnaise;
141     } __attribute__((no_randomize_layout));
142   )c");
143 
144   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
145   const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
146 
147   ASSERT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>());
148   ASSERT_FALSE(RD->isRandomized());
149   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
150 }
151 
152 TEST(RANDSTRUCT_TEST, MarkedRandomize) {
153   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
154     struct test {
155         int bacon;
156         long lettuce;
157         long long tomato;
158         float mayonnaise;
159     } __attribute__((randomize_layout));
160   )c");
161 
162   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
163 #ifdef _WIN32
164   const field_names Expected = {"lettuce", "bacon", "mayonnaise", "tomato"};
165 #else
166   const field_names Expected = {"mayonnaise", "bacon", "tomato", "lettuce"};
167 #endif
168 
169   ASSERT_TRUE(RD->hasAttr<RandomizeLayoutAttr>());
170   ASSERT_TRUE(RD->isRandomized());
171   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
172 }
173 
174 TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) {
175   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
176     struct test __attribute__((randomize_layout));
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   DiagnosticsEngine &Diags = AST->getDiagnostics();
186 
187   EXPECT_FALSE(Diags.hasFatalErrorOccurred());
188   EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
189   EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
190   EXPECT_EQ(Diags.getNumWarnings(), 1u);
191   EXPECT_EQ(Diags.getNumErrors(), 0u);
192 }
193 
194 TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) {
195   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
196     struct test2 {
197         int bacon;
198         long lettuce;
199         long long tomato;
200         float mayonnaise;
201     } __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
202   )c", true);
203 
204   DiagnosticsEngine &Diags = AST->getDiagnostics();
205 
206   EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
207   EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
208   EXPECT_EQ(Diags.getNumWarnings(), 0u);
209   EXPECT_EQ(Diags.getNumErrors(), 1u);
210 }
211 
212 TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) {
213   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
214     struct test3 {
215         int bacon;
216         long lettuce;
217         long long tomato;
218         float mayonnaise;
219     } __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
220   )c", true);
221 
222   DiagnosticsEngine &Diags = AST->getDiagnostics();
223 
224   EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
225   EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
226   EXPECT_EQ(Diags.getNumWarnings(), 0u);
227   EXPECT_EQ(Diags.getNumErrors(), 1u);
228 }
229 
230 TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) {
231   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
232     struct test {
233         int a;
234         int b;
235         int x : 1;
236         int y : 1;
237         int z : 1;
238         int c;
239     } __attribute__((randomize_layout));
240   )c");
241 
242   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
243 
244 #ifdef _WIN32
245   const field_names Expected = {"b", "a", "c", "x", "y", "z"};
246 #else
247   const field_names Expected = {"c", "x", "y", "z", "b", "a"};
248 #endif
249   const field_names Subseq = {"x", "y", "z"};
250   const field_names Actual = getFieldNamesFromRecord(RD);
251 
252   ASSERT_TRUE(isSubsequence(Actual, Subseq));
253   ASSERT_EQ(Expected, Actual);
254 }
255 
256 TEST(RANDSTRUCT_TEST, CheckVariableLengthArrayMemberRemainsAtEndOfStructure) {
257   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
258     struct test {
259         int a;
260         double b;
261         short c;
262         char name[];
263     } __attribute__((randomize_layout));
264   )c");
265 
266   const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
267 #ifdef _WIN32
268   const field_names Expected = {"b", "a", "c", "name"};
269 #else
270   const field_names Expected = {"b", "c", "a", "name"};
271 #endif
272 
273   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
274 }
275 
276 TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) {
277   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
278     struct test_struct {
279         char a;
280         float b[3];
281         short c;
282         int d;
283     } __attribute__((packed, randomize_layout));
284 
285     struct another_struct {
286         char a;
287         char b[5];
288         int c;
289     } __attribute__((packed, randomize_layout));
290 
291     struct last_struct {
292         char a;
293         long long b;
294         int c[];
295     } __attribute__((packed, randomize_layout));
296   )c");
297 
298   // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
299   // Clang's RecordBuilders can actually flesh out the information like
300   // alignment, etc.
301   {
302     const RecordDecl *RD =
303         getRecordDeclFromAST(AST->getASTContext(), "test_struct");
304     const ASTRecordLayout *Layout =
305         &AST->getASTContext().getASTRecordLayout(RD);
306 #ifdef _WIN32
307     const field_names Expected = {"a", "c", "d", "b"};
308 #else
309     const field_names Expected = {"c", "a", "d", "b"};
310 #endif
311 
312     ASSERT_EQ(19, Layout->getSize().getQuantity());
313     ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
314   }
315 
316   {
317     const RecordDecl *RD =
318         getRecordDeclFromAST(AST->getASTContext(), "another_struct");
319     const ASTRecordLayout *Layout =
320         &AST->getASTContext().getASTRecordLayout(RD);
321 #ifdef _WIN32
322     const field_names Expected = {"a", "b", "c"};
323 #else
324     const field_names Expected = {"c", "a", "b"};
325 #endif
326 
327     ASSERT_EQ(10, Layout->getSize().getQuantity());
328     ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
329   }
330 
331   {
332     const RecordDecl *RD =
333         getRecordDeclFromAST(AST->getASTContext(), "last_struct");
334     const ASTRecordLayout *Layout =
335         &AST->getASTContext().getASTRecordLayout(RD);
336     const field_names Expected = {"b", "c", "a"};
337 
338     ASSERT_EQ(9, Layout->getSize().getQuantity());
339     ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
340   }
341 }
342 
343 TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) {
344   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
345     struct test_struct {
346         int a : 1;
347         int   : 0;
348         int b : 1;
349     } __attribute__((randomize_layout));
350   )c");
351 
352   const RecordDecl *RD =
353       getRecordDeclFromAST(AST->getASTContext(), "test_struct");
354 #ifdef _WIN32
355   const field_names Expected = {"b", "a", ""};
356 #else
357   const field_names Expected = {"", "a", "b"};
358 #endif
359 
360   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
361 }
362 
363 TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) {
364   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
365     union test_union {
366         int a;
367         int b;
368         int c;
369         int d;
370         int e;
371         int f;
372     } __attribute__((randomize_layout));
373   )c");
374 
375   const RecordDecl *RD =
376       getRecordDeclFromAST(AST->getASTContext(), "test_union");
377   const field_names Expected = {"a", "b", "c", "d", "e", "f"};
378 
379   ASSERT_FALSE(RD->isRandomized());
380   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
381 }
382 
383 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
384   const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
385     struct test_struct {
386         int a;
387         struct sub_struct {
388             int b;
389             int c;
390             int d;
391             int e;
392             int f;
393         } __attribute__((randomize_layout)) s;
394         int f;
395         struct {
396             int g;
397             int h;
398             int i;
399             int j;
400             int k;
401         };
402         int l;
403         union {
404             int m;
405             int n;
406             int o;
407             int p;
408             int q;
409         };
410         int r;
411     } __attribute__((randomize_layout));
412   )c");
413 
414   const RecordDecl *RD =
415       getRecordDeclFromAST(AST->getASTContext(), "test_struct");
416 #ifdef _WIN32
417   const field_names Expected = {"", "s", "l", "", "r", "a", "f"};
418 #else
419   const field_names Expected = {"f", "a", "l", "", "", "s", "r"};
420 #endif
421 
422   ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
423 
424   bool AnonStructTested = false;
425   bool AnonUnionTested = false;
426   for (const Decl *D : RD->decls())
427     if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
428       if (const auto *Record = FD->getType()->getAs<RecordType>()) {
429         RD = Record->getDecl();
430         if (RD->isAnonymousStructOrUnion()) {
431           if (RD->isUnion()) {
432             const field_names Expected = {"m", "n", "o", "p", "q"};
433 
434             ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
435             AnonUnionTested = true;
436           } else {
437             const field_names Expected = {"g", "h", "i", "j", "k"};
438 
439             ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
440             AnonStructTested = true;
441           }
442         } else if (RD->isStruct()) {
443 #ifdef _WIN32
444           const field_names Expected = {"b", "c", "f", "d", "e"};
445 #else
446           const field_names Expected = {"d", "e", "f", "c", "b"};
447 #endif
448           ASSERT_EQ(Expected, getFieldNamesFromRecord(RD));
449         }
450       }
451     }
452 
453   ASSERT_TRUE(AnonStructTested);
454   ASSERT_TRUE(AnonUnionTested);
455 }
456 
457 } // namespace ast_matchers
458 } // namespace clang
459