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