| 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(n: 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(C: LHS->getASTContext(), Name: RecordName); |
| 83 | const RecordDecl *RHSRD = |
| 84 | getRecordDeclFromAST(C: LHS->getASTContext(), Name: RecordName); |
| 85 | |
| 86 | return getFieldNamesFromRecord(RD: LHSRD) == getFieldNamesFromRecord(RD: 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: Lang_C99); |
| 93 | Args.push_back(x: "-frandomize-layout-seed=" + std::string(Seed)); |
| 94 | |
| 95 | IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer(); |
| 96 | |
| 97 | std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs( |
| 98 | Code: SourceCode, Args, FileName: "input.c" , ToolName: "clang-tool" , |
| 99 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
| 100 | Adjuster: tooling::getClangStripDependencyFileAdjuster(), |
| 101 | VirtualMappedFiles: tooling::FileContentMappings(), DiagConsumer: &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: Lang_C99); |
| 112 | Args.push_back(x: "-frandomize-layout-seed-file=" + |
| 113 | SeedFile.getFilename().str()); |
| 114 | |
| 115 | std::unique_ptr<ASTUnit> ASTFileSeed = tooling::buildASTFromCodeWithArgs( |
| 116 | Code: SourceCode, Args, FileName: "input.c" , ToolName: "clang-tool" , |
| 117 | PCHContainerOps: std::make_shared<PCHContainerOperations>(), |
| 118 | Adjuster: tooling::getClangStripDependencyFileAdjuster(), |
| 119 | VirtualMappedFiles: tooling::FileContentMappings(), DiagConsumer: &IgnoringConsumer); |
| 120 | |
| 121 | if (!ExpectError) { |
| 122 | if (RecordNames.empty()) |
| 123 | RecordNames.push_back(x: "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 | long declCount(const RecordDecl *RD) { |
| 136 | return llvm::count_if(RD->decls(), [&](const Decl *D) { |
| 137 | return isa<FieldDecl>(Val: D) || isa<RecordDecl>(Val: D); |
| 138 | }); |
| 139 | } |
| 140 | |
| 141 | #define RANDSTRUCT_TEST_SUITE_TEST RecordLayoutRandomizationTestSuiteTest |
| 142 | |
| 143 | TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) { |
| 144 | const field_names Seq = {"a" , "b" , "c" , "d" }; |
| 145 | |
| 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" })); |
| 151 | } |
| 152 | |
| 153 | #define RANDSTRUCT_TEST RecordLayoutRandomization |
| 154 | |
| 155 | TEST(RANDSTRUCT_TEST, UnmarkedStruct) { |
| 156 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 157 | struct test { |
| 158 | int bacon; |
| 159 | long lettuce; |
| 160 | long long tomato; |
| 161 | float mayonnaise; |
| 162 | }; |
| 163 | )c" ); |
| 164 | |
| 165 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 166 | |
| 167 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 168 | long OriginalDeclCount = declCount(RD); |
| 169 | |
| 170 | EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>()); |
| 171 | EXPECT_FALSE(RD->isRandomized()); |
| 172 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 173 | } |
| 174 | |
| 175 | TEST(RANDSTRUCT_TEST, MarkedNoRandomize) { |
| 176 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 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 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 186 | |
| 187 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 188 | long OriginalDeclCount = declCount(RD); |
| 189 | |
| 190 | EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>()); |
| 191 | EXPECT_FALSE(RD->isRandomized()); |
| 192 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 193 | } |
| 194 | |
| 195 | TEST(RANDSTRUCT_TEST, MarkedRandomize) { |
| 196 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 197 | struct test { |
| 198 | int bacon; |
| 199 | long lettuce; |
| 200 | long long tomato; |
| 201 | float mayonnaise; |
| 202 | } __attribute__((randomize_layout)); |
| 203 | )c" ); |
| 204 | |
| 205 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 206 | |
| 207 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 208 | long OriginalDeclCount = declCount(RD); |
| 209 | |
| 210 | EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>()); |
| 211 | EXPECT_TRUE(RD->isRandomized()); |
| 212 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 213 | } |
| 214 | |
| 215 | TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) { |
| 216 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 217 | struct test __attribute__((randomize_layout)); |
| 218 | struct test { |
| 219 | int bacon; |
| 220 | long lettuce; |
| 221 | long long tomato; |
| 222 | float mayonnaise; |
| 223 | } __attribute__((no_randomize_layout)); |
| 224 | )c" , |
| 225 | ExpectError: true); |
| 226 | |
| 227 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 228 | |
| 229 | const DiagnosticsEngine &Diags = AST->getDiagnostics(); |
| 230 | |
| 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); |
| 236 | } |
| 237 | |
| 238 | TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) { |
| 239 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 240 | struct test { |
| 241 | int bacon; |
| 242 | long lettuce; |
| 243 | long long tomato; |
| 244 | float mayonnaise; |
| 245 | } __attribute__((randomize_layout)) __attribute__((no_randomize_layout)); |
| 246 | )c" , |
| 247 | ExpectError: true); |
| 248 | |
| 249 | EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); |
| 250 | |
| 251 | const DiagnosticsEngine &Diags = AST->getDiagnostics(); |
| 252 | |
| 253 | EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); |
| 254 | EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); |
| 255 | EXPECT_EQ(Diags.getNumWarnings(), 0u); |
| 256 | EXPECT_EQ(Diags.getNumErrors(), 1u); |
| 257 | } |
| 258 | |
| 259 | TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) { |
| 260 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 261 | struct test3 { |
| 262 | int bacon; |
| 263 | long lettuce; |
| 264 | long long tomato; |
| 265 | float mayonnaise; |
| 266 | } __attribute__((no_randomize_layout)) __attribute__((randomize_layout)); |
| 267 | )c" , |
| 268 | ExpectError: true); |
| 269 | |
| 270 | EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred()); |
| 271 | |
| 272 | const DiagnosticsEngine &Diags = AST->getDiagnostics(); |
| 273 | |
| 274 | EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); |
| 275 | EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); |
| 276 | EXPECT_EQ(Diags.getNumWarnings(), 0u); |
| 277 | EXPECT_EQ(Diags.getNumErrors(), 1u); |
| 278 | } |
| 279 | |
| 280 | TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) { |
| 281 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 282 | struct test { |
| 283 | int a; |
| 284 | int b; |
| 285 | int x : 1; |
| 286 | int y : 1; |
| 287 | int z : 1; |
| 288 | int c; |
| 289 | } __attribute__((randomize_layout)); |
| 290 | )c" ); |
| 291 | |
| 292 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 293 | |
| 294 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 295 | long OriginalDeclCount = declCount(RD); |
| 296 | |
| 297 | const field_names Actual = getFieldNamesFromRecord(RD); |
| 298 | const field_names Subseq = {"x" , "y" , "z" }; |
| 299 | |
| 300 | EXPECT_TRUE(RD->isRandomized()); |
| 301 | EXPECT_TRUE(isSubsequence(Actual, Subseq)); |
| 302 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 303 | } |
| 304 | |
| 305 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure1) { |
| 306 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 307 | struct test { |
| 308 | int a; |
| 309 | int b; |
| 310 | int c; |
| 311 | int d; |
| 312 | int e; |
| 313 | int f; |
| 314 | int g; |
| 315 | int h; |
| 316 | char name[]; |
| 317 | } __attribute__((randomize_layout)); |
| 318 | )c" ); |
| 319 | |
| 320 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 321 | |
| 322 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 323 | long OriginalDeclCount = declCount(RD); |
| 324 | |
| 325 | EXPECT_TRUE(RD->hasFlexibleArrayMember()); |
| 326 | EXPECT_TRUE(RD->isRandomized()); |
| 327 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 328 | EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name" ); |
| 329 | } |
| 330 | |
| 331 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure2) { |
| 332 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 333 | struct test { |
| 334 | int a; |
| 335 | int b; |
| 336 | int c; |
| 337 | int d; |
| 338 | int e; |
| 339 | int f; |
| 340 | int g; |
| 341 | int h; |
| 342 | char name[0]; |
| 343 | } __attribute__((randomize_layout)); |
| 344 | )c" ); |
| 345 | |
| 346 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 347 | |
| 348 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 349 | long OriginalDeclCount = declCount(RD); |
| 350 | |
| 351 | EXPECT_FALSE(RD->hasFlexibleArrayMember()); |
| 352 | EXPECT_TRUE(RD->isRandomized()); |
| 353 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 354 | EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name" ); |
| 355 | } |
| 356 | |
| 357 | TEST(RANDSTRUCT_TEST, CheckFlexibleArrayMemberRemainsAtEndOfStructure3) { |
| 358 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 359 | struct test { |
| 360 | int a; |
| 361 | int b; |
| 362 | int c; |
| 363 | int d; |
| 364 | int e; |
| 365 | int f; |
| 366 | int g; |
| 367 | int h; |
| 368 | char name[1]; |
| 369 | } __attribute__((randomize_layout)); |
| 370 | )c" ); |
| 371 | |
| 372 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 373 | |
| 374 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 375 | long OriginalDeclCount = declCount(RD); |
| 376 | |
| 377 | EXPECT_FALSE(RD->hasFlexibleArrayMember()); |
| 378 | EXPECT_TRUE(RD->isRandomized()); |
| 379 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 380 | EXPECT_EQ(getFieldNamesFromRecord(RD).back(), "name" ); |
| 381 | } |
| 382 | |
| 383 | TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) { |
| 384 | std::unique_ptr<ASTUnit> AST = |
| 385 | makeAST(SourceCode: R"c( |
| 386 | struct test_struct { |
| 387 | char a; |
| 388 | float b[3]; |
| 389 | short c; |
| 390 | int d; |
| 391 | } __attribute__((packed, randomize_layout)); |
| 392 | |
| 393 | struct another_struct { |
| 394 | char a; |
| 395 | char b[5]; |
| 396 | int c; |
| 397 | } __attribute__((packed, randomize_layout)); |
| 398 | |
| 399 | struct last_struct { |
| 400 | char a; |
| 401 | long long b; |
| 402 | int c[]; |
| 403 | } __attribute__((packed, randomize_layout)); |
| 404 | )c" , |
| 405 | ExpectError: false, |
| 406 | RecordNames: std::vector<std::string>( |
| 407 | {"test_struct" , "another_struct" , "last_struct" })); |
| 408 | |
| 409 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 410 | |
| 411 | // FIXME (?): calling getASTRecordLayout is probably a necessary evil so that |
| 412 | // Clang's RecordBuilders can actually flesh out the information like |
| 413 | // alignment, etc. |
| 414 | { |
| 415 | const RecordDecl *RD = |
| 416 | getRecordDeclFromAST(C: AST->getASTContext(), Name: "test_struct" ); |
| 417 | const ASTRecordLayout *Layout = |
| 418 | &AST->getASTContext().getASTRecordLayout(D: RD); |
| 419 | long OriginalDeclCount = declCount(RD); |
| 420 | |
| 421 | EXPECT_TRUE(RD->isRandomized()); |
| 422 | EXPECT_EQ(19, Layout->getSize().getQuantity()); |
| 423 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 424 | } |
| 425 | |
| 426 | { |
| 427 | const RecordDecl *RD = |
| 428 | getRecordDeclFromAST(C: AST->getASTContext(), Name: "another_struct" ); |
| 429 | const ASTRecordLayout *Layout = |
| 430 | &AST->getASTContext().getASTRecordLayout(D: RD); |
| 431 | long OriginalDeclCount = declCount(RD); |
| 432 | |
| 433 | EXPECT_TRUE(RD->isRandomized()); |
| 434 | EXPECT_EQ(10, Layout->getSize().getQuantity()); |
| 435 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 436 | } |
| 437 | |
| 438 | { |
| 439 | const RecordDecl *RD = |
| 440 | getRecordDeclFromAST(C: AST->getASTContext(), Name: "last_struct" ); |
| 441 | const ASTRecordLayout *Layout = |
| 442 | &AST->getASTContext().getASTRecordLayout(D: RD); |
| 443 | long OriginalDeclCount = declCount(RD); |
| 444 | |
| 445 | EXPECT_TRUE(RD->isRandomized()); |
| 446 | EXPECT_EQ(9, Layout->getSize().getQuantity()); |
| 447 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) { |
| 452 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 453 | struct test { |
| 454 | int a : 1; |
| 455 | int : 0; |
| 456 | int b : 1; |
| 457 | } __attribute__((randomize_layout)); |
| 458 | )c" ); |
| 459 | |
| 460 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 461 | |
| 462 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 463 | long OriginalDeclCount = declCount(RD); |
| 464 | |
| 465 | EXPECT_TRUE(RD->isRandomized()); |
| 466 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 467 | } |
| 468 | |
| 469 | TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) { |
| 470 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 471 | union test { |
| 472 | int a; |
| 473 | int b; |
| 474 | int c; |
| 475 | int d; |
| 476 | int e; |
| 477 | int f; |
| 478 | } __attribute__((randomize_layout)); |
| 479 | )c" ); |
| 480 | |
| 481 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 482 | |
| 483 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 484 | long OriginalDeclCount = declCount(RD); |
| 485 | |
| 486 | EXPECT_FALSE(RD->isRandomized()); |
| 487 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 488 | } |
| 489 | |
| 490 | TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) { |
| 491 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 492 | struct test { |
| 493 | int a; |
| 494 | struct sub_struct { |
| 495 | int b; |
| 496 | int c; |
| 497 | int d; |
| 498 | int e; |
| 499 | int f; |
| 500 | } __attribute__((randomize_layout)) s; |
| 501 | int f; |
| 502 | struct { |
| 503 | int g; |
| 504 | int h; |
| 505 | int i; |
| 506 | int j; |
| 507 | int k; |
| 508 | }; |
| 509 | int l; |
| 510 | union { |
| 511 | int m; |
| 512 | int n; |
| 513 | int o; |
| 514 | int p; |
| 515 | int q; |
| 516 | }; |
| 517 | int r; |
| 518 | } __attribute__((randomize_layout)); |
| 519 | )c" ); |
| 520 | |
| 521 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 522 | |
| 523 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 524 | long OriginalDeclCount = declCount(RD); |
| 525 | |
| 526 | EXPECT_TRUE(RD->isRandomized()); |
| 527 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 528 | |
| 529 | bool AnonStructTested = false; |
| 530 | bool AnonUnionTested = false; |
| 531 | |
| 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. |
| 538 | if (RD->isUnion()) { |
| 539 | const field_names Expected = {"m" , "n" , "o" , "p" , "q" }; |
| 540 | |
| 541 | EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); |
| 542 | AnonUnionTested = true; |
| 543 | } else { |
| 544 | const field_names Expected = {"g" , "h" , "i" , "j" , "k" }; |
| 545 | |
| 546 | EXPECT_EQ(Expected, getFieldNamesFromRecord(RD)); |
| 547 | AnonStructTested = true; |
| 548 | } |
| 549 | } |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | EXPECT_TRUE(AnonStructTested); |
| 554 | EXPECT_TRUE(AnonUnionTested); |
| 555 | } |
| 556 | |
| 557 | TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsReferenced) { |
| 558 | std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 559 | struct test { |
| 560 | int bacon; |
| 561 | long lettuce; |
| 562 | struct { double avocado; char blech; }; |
| 563 | long long tomato; |
| 564 | union { char toast[8]; unsigned toast_thing; }; |
| 565 | float mayonnaise; |
| 566 | } __attribute__((randomize_layout)); |
| 567 | |
| 568 | int foo(struct test *t) { |
| 569 | return t->blech; |
| 570 | } |
| 571 | |
| 572 | char *bar(struct test *t) { |
| 573 | return t->toast; |
| 574 | } |
| 575 | )c" ); |
| 576 | |
| 577 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 578 | |
| 579 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 580 | long OriginalDeclCount = declCount(RD); |
| 581 | |
| 582 | EXPECT_TRUE(RD->isRandomized()); |
| 583 | EXPECT_EQ(OriginalDeclCount, declCount(RD)); |
| 584 | } |
| 585 | |
| 586 | TEST(RANDSTRUCT_TEST, AutoRandomizeStructOfFunctionPointers) { |
| 587 | const std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 588 | typedef void (*func_ptr)(); |
| 589 | |
| 590 | struct test { |
| 591 | func_ptr a; |
| 592 | func_ptr b; |
| 593 | func_ptr c; |
| 594 | func_ptr d; |
| 595 | func_ptr e; |
| 596 | func_ptr f; |
| 597 | func_ptr g; |
| 598 | }; |
| 599 | )c" ); |
| 600 | |
| 601 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 602 | |
| 603 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 604 | |
| 605 | EXPECT_TRUE(RD->isRandomized()); |
| 606 | } |
| 607 | |
| 608 | TEST(RANDSTRUCT_TEST, DisableAutoRandomizeStructOfFunctionPointers) { |
| 609 | const std::unique_ptr<ASTUnit> AST = makeAST(SourceCode: R"c( |
| 610 | typedef void (*func_ptr)(); |
| 611 | |
| 612 | struct test { |
| 613 | func_ptr a; |
| 614 | func_ptr b; |
| 615 | func_ptr c; |
| 616 | func_ptr d; |
| 617 | func_ptr e; |
| 618 | func_ptr f; |
| 619 | func_ptr g; |
| 620 | } __attribute__((no_randomize_layout)); |
| 621 | )c" ); |
| 622 | |
| 623 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
| 624 | |
| 625 | const RecordDecl *RD = getRecordDeclFromAST(C: AST->getASTContext(), Name: "test" ); |
| 626 | |
| 627 | EXPECT_FALSE(RD->isRandomized()); |
| 628 | } |
| 629 | |
| 630 | } // namespace ast_matchers |
| 631 | } // namespace clang |
| 632 | |