17aa8c38aSConnor Kuehl //===--- Randstruct.cpp ---------------------------------------------------===//
27aa8c38aSConnor Kuehl //
37aa8c38aSConnor Kuehl // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47aa8c38aSConnor Kuehl // See https://llvm.org/LICENSE.txt for license information.
57aa8c38aSConnor Kuehl // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67aa8c38aSConnor Kuehl //
77aa8c38aSConnor Kuehl //===----------------------------------------------------------------------===//
87aa8c38aSConnor Kuehl //
97aa8c38aSConnor Kuehl // This file contains the implementation for Clang's structure field layout
107aa8c38aSConnor Kuehl // randomization.
117aa8c38aSConnor Kuehl //
127aa8c38aSConnor Kuehl //===----------------------------------------------------------------------===//
137aa8c38aSConnor Kuehl 
147aa8c38aSConnor Kuehl #include "clang/AST/Randstruct.h"
157aa8c38aSConnor Kuehl #include "clang/AST/ASTContext.h"
167aa8c38aSConnor Kuehl #include "clang/AST/ASTDiagnostic.h"
177aa8c38aSConnor Kuehl #include "clang/AST/Attr.h"
18*463790bfSBill Wendling #include "clang/AST/Decl.h"
19*463790bfSBill Wendling #include "clang/AST/DeclCXX.h" // For StaticAssertDecl
207aa8c38aSConnor Kuehl #include "clang/Basic/Diagnostic.h"
217aa8c38aSConnor Kuehl #include "llvm/ADT/SmallVector.h"
227aa8c38aSConnor Kuehl 
237aa8c38aSConnor Kuehl #include <algorithm>
247aa8c38aSConnor Kuehl #include <random>
257aa8c38aSConnor Kuehl #include <set>
267aa8c38aSConnor Kuehl #include <sstream>
277aa8c38aSConnor Kuehl #include <string>
287aa8c38aSConnor Kuehl 
297aa8c38aSConnor Kuehl using clang::ASTContext;
307aa8c38aSConnor Kuehl using clang::FieldDecl;
317aa8c38aSConnor Kuehl using llvm::SmallVector;
327aa8c38aSConnor Kuehl 
337aa8c38aSConnor Kuehl namespace {
347aa8c38aSConnor Kuehl 
357aa8c38aSConnor Kuehl // FIXME: Replace this with some discovery once that mechanism exists.
367aa8c38aSConnor Kuehl enum { CACHE_LINE = 64 };
377aa8c38aSConnor Kuehl 
387aa8c38aSConnor Kuehl // The Bucket class holds the struct fields we're trying to fill to a
397aa8c38aSConnor Kuehl // cache-line.
407aa8c38aSConnor Kuehl class Bucket {
417aa8c38aSConnor Kuehl   SmallVector<FieldDecl *, 64> Fields;
427aa8c38aSConnor Kuehl   int Size = 0;
437aa8c38aSConnor Kuehl 
447aa8c38aSConnor Kuehl public:
457aa8c38aSConnor Kuehl   virtual ~Bucket() = default;
467aa8c38aSConnor Kuehl 
fields()477aa8c38aSConnor Kuehl   SmallVector<FieldDecl *, 64> &fields() { return Fields; }
487aa8c38aSConnor Kuehl   void addField(FieldDecl *Field, int FieldSize);
canFit(int FieldSize) const497aa8c38aSConnor Kuehl   virtual bool canFit(int FieldSize) const {
507aa8c38aSConnor Kuehl     return Size + FieldSize <= CACHE_LINE;
517aa8c38aSConnor Kuehl   }
isBitfieldRun() const527aa8c38aSConnor Kuehl   virtual bool isBitfieldRun() const { return false; }
full() const537aa8c38aSConnor Kuehl   bool full() const { return Size >= CACHE_LINE; }
547aa8c38aSConnor Kuehl };
557aa8c38aSConnor Kuehl 
addField(FieldDecl * Field,int FieldSize)567aa8c38aSConnor Kuehl void Bucket::addField(FieldDecl *Field, int FieldSize) {
577aa8c38aSConnor Kuehl   Size += FieldSize;
587aa8c38aSConnor Kuehl   Fields.push_back(Field);
597aa8c38aSConnor Kuehl }
607aa8c38aSConnor Kuehl 
617aa8c38aSConnor Kuehl struct BitfieldRunBucket : public Bucket {
canFit__anon9f9330eb0111::BitfieldRunBucket627aa8c38aSConnor Kuehl   bool canFit(int FieldSize) const override { return true; }
isBitfieldRun__anon9f9330eb0111::BitfieldRunBucket637aa8c38aSConnor Kuehl   bool isBitfieldRun() const override { return true; }
647aa8c38aSConnor Kuehl };
657aa8c38aSConnor Kuehl 
randomizeStructureLayoutImpl(const ASTContext & Context,llvm::SmallVectorImpl<FieldDecl * > & FieldsOut,std::mt19937 & RNG)667aa8c38aSConnor Kuehl void randomizeStructureLayoutImpl(const ASTContext &Context,
677aa8c38aSConnor Kuehl                                   llvm::SmallVectorImpl<FieldDecl *> &FieldsOut,
687aa8c38aSConnor Kuehl                                   std::mt19937 &RNG) {
697aa8c38aSConnor Kuehl   // All of the Buckets produced by best-effort cache-line algorithm.
707aa8c38aSConnor Kuehl   SmallVector<std::unique_ptr<Bucket>, 16> Buckets;
717aa8c38aSConnor Kuehl 
727aa8c38aSConnor Kuehl   // The current bucket of fields that we are trying to fill to a cache-line.
737aa8c38aSConnor Kuehl   std::unique_ptr<Bucket> CurrentBucket;
747aa8c38aSConnor Kuehl 
757aa8c38aSConnor Kuehl   // The current bucket containing the run of adjacent bitfields to ensure they
767aa8c38aSConnor Kuehl   // remain adjacent.
777aa8c38aSConnor Kuehl   std::unique_ptr<BitfieldRunBucket> CurrentBitfieldRun;
787aa8c38aSConnor Kuehl 
797aa8c38aSConnor Kuehl   // Tracks the number of fields that we failed to fit to the current bucket,
807aa8c38aSConnor Kuehl   // and thus still need to be added later.
817aa8c38aSConnor Kuehl   size_t Skipped = 0;
827aa8c38aSConnor Kuehl 
837aa8c38aSConnor Kuehl   while (!FieldsOut.empty()) {
847aa8c38aSConnor Kuehl     // If we've Skipped more fields than we have remaining to place, that means
857aa8c38aSConnor Kuehl     // that they can't fit in our current bucket, and we need to start a new
867aa8c38aSConnor Kuehl     // one.
877aa8c38aSConnor Kuehl     if (Skipped >= FieldsOut.size()) {
887aa8c38aSConnor Kuehl       Skipped = 0;
897aa8c38aSConnor Kuehl       Buckets.push_back(std::move(CurrentBucket));
907aa8c38aSConnor Kuehl     }
917aa8c38aSConnor Kuehl 
927aa8c38aSConnor Kuehl     // Take the first field that needs to be put in a bucket.
937aa8c38aSConnor Kuehl     auto FieldIter = FieldsOut.begin();
947aa8c38aSConnor Kuehl     FieldDecl *FD = *FieldIter;
957aa8c38aSConnor Kuehl 
967aa8c38aSConnor Kuehl     if (FD->isBitField() && !FD->isZeroLengthBitField(Context)) {
977aa8c38aSConnor Kuehl       // Start a bitfield run if this is the first bitfield we have found.
987aa8c38aSConnor Kuehl       if (!CurrentBitfieldRun)
997aa8c38aSConnor Kuehl         CurrentBitfieldRun = std::make_unique<BitfieldRunBucket>();
1007aa8c38aSConnor Kuehl 
1017aa8c38aSConnor Kuehl       // We've placed the field, and can remove it from the "awaiting Buckets"
1027aa8c38aSConnor Kuehl       // vector called "Fields."
1037aa8c38aSConnor Kuehl       CurrentBitfieldRun->addField(FD, /*FieldSize is irrelevant here*/ 1);
1047aa8c38aSConnor Kuehl       FieldsOut.erase(FieldIter);
1057aa8c38aSConnor Kuehl       continue;
1067aa8c38aSConnor Kuehl     }
1077aa8c38aSConnor Kuehl 
1087aa8c38aSConnor Kuehl     // Else, current field is not a bitfield. If we were previously in a
1097aa8c38aSConnor Kuehl     // bitfield run, end it.
1107aa8c38aSConnor Kuehl     if (CurrentBitfieldRun)
1117aa8c38aSConnor Kuehl       Buckets.push_back(std::move(CurrentBitfieldRun));
1127aa8c38aSConnor Kuehl 
1137aa8c38aSConnor Kuehl     // If we don't have a bucket, make one.
1147aa8c38aSConnor Kuehl     if (!CurrentBucket)
1157aa8c38aSConnor Kuehl       CurrentBucket = std::make_unique<Bucket>();
1167aa8c38aSConnor Kuehl 
1177aa8c38aSConnor Kuehl     uint64_t Width = Context.getTypeInfo(FD->getType()).Width;
1187aa8c38aSConnor Kuehl     if (Width >= CACHE_LINE) {
1197aa8c38aSConnor Kuehl       std::unique_ptr<Bucket> OverSized = std::make_unique<Bucket>();
1207aa8c38aSConnor Kuehl       OverSized->addField(FD, Width);
1217aa8c38aSConnor Kuehl       FieldsOut.erase(FieldIter);
1227aa8c38aSConnor Kuehl       Buckets.push_back(std::move(OverSized));
1237aa8c38aSConnor Kuehl       continue;
1247aa8c38aSConnor Kuehl     }
1257aa8c38aSConnor Kuehl 
1267aa8c38aSConnor Kuehl     // If it fits, add it.
1277aa8c38aSConnor Kuehl     if (CurrentBucket->canFit(Width)) {
1287aa8c38aSConnor Kuehl       CurrentBucket->addField(FD, Width);
1297aa8c38aSConnor Kuehl       FieldsOut.erase(FieldIter);
1307aa8c38aSConnor Kuehl 
1317aa8c38aSConnor Kuehl       // If it's now full, tie off the bucket.
1327aa8c38aSConnor Kuehl       if (CurrentBucket->full()) {
1337aa8c38aSConnor Kuehl         Skipped = 0;
1347aa8c38aSConnor Kuehl         Buckets.push_back(std::move(CurrentBucket));
1357aa8c38aSConnor Kuehl       }
1367aa8c38aSConnor Kuehl     } else {
1377aa8c38aSConnor Kuehl       // We can't fit it in our current bucket. Move to the end for processing
1387aa8c38aSConnor Kuehl       // later.
1397aa8c38aSConnor Kuehl       ++Skipped; // Mark it skipped.
1407aa8c38aSConnor Kuehl       FieldsOut.push_back(FD);
1417aa8c38aSConnor Kuehl       FieldsOut.erase(FieldIter);
1427aa8c38aSConnor Kuehl     }
1437aa8c38aSConnor Kuehl   }
1447aa8c38aSConnor Kuehl 
1457aa8c38aSConnor Kuehl   // Done processing the fields awaiting a bucket.
1467aa8c38aSConnor Kuehl 
1477aa8c38aSConnor Kuehl   // If we were filling a bucket, tie it off.
1487aa8c38aSConnor Kuehl   if (CurrentBucket)
1497aa8c38aSConnor Kuehl     Buckets.push_back(std::move(CurrentBucket));
1507aa8c38aSConnor Kuehl 
1517aa8c38aSConnor Kuehl   // If we were processing a bitfield run bucket, tie it off.
1527aa8c38aSConnor Kuehl   if (CurrentBitfieldRun)
1537aa8c38aSConnor Kuehl     Buckets.push_back(std::move(CurrentBitfieldRun));
1547aa8c38aSConnor Kuehl 
1558dbc6b56SNico Weber   std::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
1567aa8c38aSConnor Kuehl 
1577aa8c38aSConnor Kuehl   // Produce the new ordering of the elements from the Buckets.
1587aa8c38aSConnor Kuehl   SmallVector<FieldDecl *, 16> FinalOrder;
1597aa8c38aSConnor Kuehl   for (const std::unique_ptr<Bucket> &B : Buckets) {
1607aa8c38aSConnor Kuehl     llvm::SmallVectorImpl<FieldDecl *> &RandFields = B->fields();
1617aa8c38aSConnor Kuehl     if (!B->isBitfieldRun())
1628dbc6b56SNico Weber       std::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
1637aa8c38aSConnor Kuehl 
1647aa8c38aSConnor Kuehl     FinalOrder.insert(FinalOrder.end(), RandFields.begin(), RandFields.end());
1657aa8c38aSConnor Kuehl   }
1667aa8c38aSConnor Kuehl 
1677aa8c38aSConnor Kuehl   FieldsOut = FinalOrder;
1687aa8c38aSConnor Kuehl }
1697aa8c38aSConnor Kuehl 
1707aa8c38aSConnor Kuehl } // anonymous namespace
1717aa8c38aSConnor Kuehl 
1727aa8c38aSConnor Kuehl namespace clang {
1737aa8c38aSConnor Kuehl namespace randstruct {
1747aa8c38aSConnor Kuehl 
randomizeStructureLayout(const ASTContext & Context,RecordDecl * RD,SmallVectorImpl<Decl * > & FinalOrdering)175*463790bfSBill Wendling bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD,
1767aa8c38aSConnor Kuehl                               SmallVectorImpl<Decl *> &FinalOrdering) {
1777aa8c38aSConnor Kuehl   SmallVector<FieldDecl *, 64> RandomizedFields;
178*463790bfSBill Wendling   SmallVector<Decl *, 8> PostRandomizedFields;
1797aa8c38aSConnor Kuehl 
1807aa8c38aSConnor Kuehl   unsigned TotalNumFields = 0;
181*463790bfSBill Wendling   for (Decl *D : RD->decls()) {
1827aa8c38aSConnor Kuehl     ++TotalNumFields;
1837aa8c38aSConnor Kuehl     if (auto *FD = dyn_cast<FieldDecl>(D))
1847aa8c38aSConnor Kuehl       RandomizedFields.push_back(FD);
185*463790bfSBill Wendling     else if (isa<StaticAssertDecl>(D) || isa<IndirectFieldDecl>(D))
186*463790bfSBill Wendling       PostRandomizedFields.push_back(D);
1877aa8c38aSConnor Kuehl     else
1887aa8c38aSConnor Kuehl       FinalOrdering.push_back(D);
1897aa8c38aSConnor Kuehl   }
1907aa8c38aSConnor Kuehl 
1917aa8c38aSConnor Kuehl   if (RandomizedFields.empty())
1927aa8c38aSConnor Kuehl     return false;
1937aa8c38aSConnor Kuehl 
194*463790bfSBill Wendling   // Struct might end with a flexible array or an array of size 0 or 1,
1957aa8c38aSConnor Kuehl   // in which case we don't want to randomize it.
196*463790bfSBill Wendling   FieldDecl *FlexibleArray =
197*463790bfSBill Wendling       RD->hasFlexibleArrayMember() ? RandomizedFields.pop_back_val() : nullptr;
198*463790bfSBill Wendling   if (!FlexibleArray) {
199*463790bfSBill Wendling     if (const auto *CA =
200*463790bfSBill Wendling             dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType()))
201*463790bfSBill Wendling       if (CA->getSize().sle(2))
202*463790bfSBill Wendling         FlexibleArray = RandomizedFields.pop_back_val();
2037aa8c38aSConnor Kuehl   }
2047aa8c38aSConnor Kuehl 
205*463790bfSBill Wendling   std::string Seed =
206*463790bfSBill Wendling       Context.getLangOpts().RandstructSeed + RD->getNameAsString();
2077aa8c38aSConnor Kuehl   std::seed_seq SeedSeq(Seed.begin(), Seed.end());
2087aa8c38aSConnor Kuehl   std::mt19937 RNG(SeedSeq);
2097aa8c38aSConnor Kuehl 
2107aa8c38aSConnor Kuehl   randomizeStructureLayoutImpl(Context, RandomizedFields, RNG);
2117aa8c38aSConnor Kuehl 
212*463790bfSBill Wendling   // Plorp the randomized decls into the final ordering.
2137aa8c38aSConnor Kuehl   FinalOrdering.insert(FinalOrdering.end(), RandomizedFields.begin(),
2147aa8c38aSConnor Kuehl                        RandomizedFields.end());
2157aa8c38aSConnor Kuehl 
216*463790bfSBill Wendling   // Add fields that belong towards the end of the RecordDecl.
217*463790bfSBill Wendling   FinalOrdering.insert(FinalOrdering.end(), PostRandomizedFields.begin(),
218*463790bfSBill Wendling                        PostRandomizedFields.end());
219*463790bfSBill Wendling 
220*463790bfSBill Wendling   // Add back the flexible array.
221*463790bfSBill Wendling   if (FlexibleArray)
222*463790bfSBill Wendling     FinalOrdering.push_back(FlexibleArray);
223*463790bfSBill Wendling 
2247aa8c38aSConnor Kuehl   assert(TotalNumFields == FinalOrdering.size() &&
2257aa8c38aSConnor Kuehl          "Decl count has been altered after Randstruct randomization!");
226c98b601bSFangrui Song   (void)TotalNumFields;
2277aa8c38aSConnor Kuehl   return true;
2287aa8c38aSConnor Kuehl }
2297aa8c38aSConnor Kuehl 
2307aa8c38aSConnor Kuehl } // end namespace randstruct
2317aa8c38aSConnor Kuehl } // end namespace clang
232