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