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