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(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(LHS->getASTContext(), RecordName); 83 const RecordDecl *RHSRD = 84 getRecordDeclFromAST(LHS->getASTContext(), RecordName); 85 86 return getFieldNamesFromRecord(LHSRD) == getFieldNamesFromRecord(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_C99); 93 Args.push_back("-frandomize-layout-seed=" + std::string(Seed)); 94 95 IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer(); 96 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); 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_C99); 112 Args.push_back("-frandomize-layout-seed-file=" + 113 SeedFile.getFilename().str()); 114 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); 120 121 if (!ExpectError) { 122 if (RecordNames.empty()) 123 RecordNames.push_back("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 #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest 136 137 TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) { 138 const field_names Seq = {"a", "b", "c", "d"}; 139 140 EXPECT_TRUE(isSubsequence(Seq, {"b", "c"})); 141 EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"})); 142 EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"})); 143 EXPECT_TRUE(isSubsequence(Seq, {"a"})); 144 EXPECT_FALSE(isSubsequence(Seq, {"a", "d"})); 145 } 146 147 #define RANDSTRUCT_TEST RecordLayoutRandomization 148 149 TEST(RANDSTRUCT_TEST, UnmarkedStruct) { 150 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 151 struct test { 152 int bacon; 153 long lettuce; 154 long long tomato; 155 float mayonnaise; 156 }; 157 )c"); 158 159 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 160 161 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 162 const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"}; 163 164 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 165 EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>()); 166 EXPECT_FALSE(RD->isRandomized()); 167 } 168 169 TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { 170 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 171 struct test { 172 int bacon; 173 long lettuce; 174 long long tomato; 175 float mayonnaise; 176 } __attribute__((no_randomize_layout)); 177 )c"); 178 179 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 180 181 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 182 const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"}; 183 184 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 185 EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>()); 186 EXPECT_FALSE(RD->isRandomized()); 187 } 188 189 TEST(RANDSTRUCT_TEST, MarkedRandomize) { 190 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 191 struct test { 192 int bacon; 193 long lettuce; 194 long long tomato; 195 float mayonnaise; 196 } __attribute__((randomize_layout)); 197 )c"); 198 199 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 200 201 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 202 const field_names Expected = {"lettuce", "bacon", "mayonnaise", "tomato"}; 203 204 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 205 EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>()); 206 EXPECT_TRUE(RD->isRandomized()); 207 } 208 209 TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) { 210 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 211 struct test __attribute__((randomize_layout)); 212 struct test { 213 int bacon; 214 long lettuce; 215 long long tomato; 216 float mayonnaise; 217 } __attribute__((no_randomize_layout)); 218 )c", true); 219 220 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 221 222 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 223 224 EXPECT_FALSE(Diags.hasFatalErrorOccurred()); 225 EXPECT_FALSE(Diags.hasUncompilableErrorOccurred()); 226 EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); 227 EXPECT_EQ(Diags.getNumWarnings(), 1u); 228 EXPECT_EQ(Diags.getNumErrors(), 0u); 229 } 230 231 TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) { 232 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 233 struct test { 234 int bacon; 235 long lettuce; 236 long long tomato; 237 float mayonnaise; 238 } __attribute__((randomize_layout)) __attribute__((no_randomize_layout)); 239 )c", true); 240 241 EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); 242 243 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 244 245 EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); 246 EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); 247 EXPECT_EQ(Diags.getNumWarnings(), 0u); 248 EXPECT_EQ(Diags.getNumErrors(), 1u); 249 } 250 251 TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) { 252 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 253 struct test3 { 254 int bacon; 255 long lettuce; 256 long long tomato; 257 float mayonnaise; 258 } __attribute__((no_randomize_layout)) __attribute__((randomize_layout)); 259 )c", true); 260 261 EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); 262 263 const DiagnosticsEngine &Diags = AST->getDiagnostics(); 264 265 EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); 266 EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); 267 EXPECT_EQ(Diags.getNumWarnings(), 0u); 268 EXPECT_EQ(Diags.getNumErrors(), 1u); 269 } 270 271 TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) { 272 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 273 struct test { 274 int a; 275 int b; 276 int x : 1; 277 int y : 1; 278 int z : 1; 279 int c; 280 } __attribute__((randomize_layout)); 281 )c"); 282 283 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 284 285 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 286 const field_names Expected = { 287 "a", "b", "c", "x", "y", "z" // x, y, z needs to be a subsequnce. 288 }; 289 290 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 291 EXPECT_TRUE(RD->isRandomized()); 292 } 293 294 // FIXME: Enable when fix for flexible arrays is submitted. 295 TEST(RANDSTRUCT_TEST, DISABLED_CheckVariableLengthArrayMemberRemainsAtEndOfStructure) { 296 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 297 struct test { 298 int a; 299 double b; 300 short c; 301 char name[]; 302 } __attribute__((randomize_layout)); 303 )c"); 304 305 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 306 307 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 308 const field_names Expected = {"c", "a", "b", "name"}; 309 310 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 311 EXPECT_TRUE(RD->isRandomized()); 312 } 313 314 TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { 315 std::unique_ptr<ASTUnit> AST = 316 makeAST(R"c( 317 struct test_struct { 318 char a; 319 float b[3]; 320 short c; 321 int d; 322 } __attribute__((packed, randomize_layout)); 323 324 struct another_struct { 325 char a; 326 char b[5]; 327 int c; 328 } __attribute__((packed, randomize_layout)); 329 330 struct last_struct { 331 char a; 332 long long b; 333 int c[]; 334 } __attribute__((packed, randomize_layout)); 335 )c", false, field_names({"test_struct", "another_struct", "last_struct"})); 336 337 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 338 339 // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that 340 // Clang's RecordBuilders can actually flesh out the information like 341 // alignment, etc. 342 { 343 const RecordDecl *RD = 344 getRecordDeclFromAST(AST->getASTContext(), "test_struct"); 345 const ASTRecordLayout *Layout = 346 &AST->getASTContext().getASTRecordLayout(RD); 347 const field_names Expected = {"b", "a", "c", "d"}; 348 349 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 350 EXPECT_TRUE(RD->isRandomized()); 351 EXPECT_EQ(19, Layout->getSize().getQuantity()); 352 } 353 354 { 355 const RecordDecl *RD = 356 getRecordDeclFromAST(AST->getASTContext(), "another_struct"); 357 const ASTRecordLayout *Layout = 358 &AST->getASTContext().getASTRecordLayout(RD); 359 const field_names Expected = {"c", "b", "a"}; 360 361 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 362 EXPECT_TRUE(RD->isRandomized()); 363 EXPECT_EQ(10, Layout->getSize().getQuantity()); 364 } 365 366 { 367 const RecordDecl *RD = 368 getRecordDeclFromAST(AST->getASTContext(), "last_struct"); 369 const ASTRecordLayout *Layout = 370 &AST->getASTContext().getASTRecordLayout(RD); 371 const field_names Expected = {"a", "c", "b"}; 372 373 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 374 EXPECT_TRUE(RD->isRandomized()); 375 EXPECT_EQ(9, Layout->getSize().getQuantity()); 376 } 377 } 378 379 TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) { 380 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 381 struct test { 382 int a : 1; 383 int : 0; 384 int b : 1; 385 } __attribute__((randomize_layout)); 386 )c"); 387 388 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 389 390 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 391 const field_names Expected = {"a", "b", ""}; 392 393 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 394 EXPECT_TRUE(RD->isRandomized()); 395 } 396 397 TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) { 398 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 399 union test { 400 int a; 401 int b; 402 int c; 403 int d; 404 int e; 405 int f; 406 } __attribute__((randomize_layout)); 407 )c"); 408 409 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 410 411 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 412 const field_names Expected = {"a", "b", "c", "d", "e", "f"}; 413 414 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 415 EXPECT_FALSE(RD->isRandomized()); 416 } 417 418 TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) { 419 std::unique_ptr<ASTUnit> AST = makeAST(R"c( 420 struct test { 421 int a; 422 struct sub_struct { 423 int b; 424 int c; 425 int d; 426 int e; 427 int f; 428 } __attribute__((randomize_layout)) s; 429 int f; 430 struct { 431 int g; 432 int h; 433 int i; 434 int j; 435 int k; 436 }; 437 int l; 438 union { 439 int m; 440 int n; 441 int o; 442 int p; 443 int q; 444 }; 445 int r; 446 } __attribute__((randomize_layout)); 447 )c"); 448 449 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 450 451 const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test"); 452 const field_names Expected = {"", "l", "", "r", "s", "a", "f"}; 453 454 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 455 EXPECT_TRUE(RD->isRandomized()); 456 457 bool AnonStructTested = false; 458 bool AnonUnionTested = false; 459 460 for (const Decl *D : RD->decls()) 461 if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) { 462 if (const auto *Record = FD->getType()->getAs<RecordType>()) { 463 RD = Record->getDecl(); 464 if (RD->isAnonymousStructOrUnion()) { 465 // These field orders shouldn't change. 466 if (RD->isUnion()) { 467 const field_names Expected = {"m", "n", "o", "p", "q"}; 468 469 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 470 AnonUnionTested = true; 471 } else { 472 const field_names Expected = {"g", "h", "i", "j", "k"}; 473 474 EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); 475 AnonStructTested = true; 476 } 477 } 478 } 479 } 480 481 EXPECT_TRUE(AnonStructTested); 482 EXPECT_TRUE(AnonUnionTested); 483 } 484 485 } // namespace ast_matchers 486 } // namespace clang 487