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