xref: /freebsd-src/contrib/llvm-project/clang/lib/AST/Randstruct.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
1*81ad6265SDimitry Andric //===--- Randstruct.cpp ---------------------------------------------------===//
2*81ad6265SDimitry Andric //
3*81ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*81ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*81ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*81ad6265SDimitry Andric //
7*81ad6265SDimitry Andric //===----------------------------------------------------------------------===//
8*81ad6265SDimitry Andric //
9*81ad6265SDimitry Andric // This file contains the implementation for Clang's structure field layout
10*81ad6265SDimitry Andric // randomization.
11*81ad6265SDimitry Andric //
12*81ad6265SDimitry Andric //===----------------------------------------------------------------------===//
13*81ad6265SDimitry Andric 
14*81ad6265SDimitry Andric #include "clang/AST/Randstruct.h"
15*81ad6265SDimitry Andric #include "clang/AST/ASTContext.h"
16*81ad6265SDimitry Andric #include "clang/AST/ASTDiagnostic.h"
17*81ad6265SDimitry Andric #include "clang/AST/Attr.h"
18*81ad6265SDimitry Andric #include "clang/AST/Decl.h"
19*81ad6265SDimitry Andric #include "clang/AST/DeclCXX.h" // For StaticAssertDecl
20*81ad6265SDimitry Andric #include "clang/Basic/Diagnostic.h"
21*81ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h"
22*81ad6265SDimitry Andric 
23*81ad6265SDimitry Andric #include <algorithm>
24*81ad6265SDimitry Andric #include <random>
25*81ad6265SDimitry Andric #include <set>
26*81ad6265SDimitry Andric #include <sstream>
27*81ad6265SDimitry Andric #include <string>
28*81ad6265SDimitry Andric 
29*81ad6265SDimitry Andric using clang::ASTContext;
30*81ad6265SDimitry Andric using clang::FieldDecl;
31*81ad6265SDimitry Andric using llvm::SmallVector;
32*81ad6265SDimitry Andric 
33*81ad6265SDimitry Andric namespace {
34*81ad6265SDimitry Andric 
35*81ad6265SDimitry Andric // FIXME: Replace this with some discovery once that mechanism exists.
36*81ad6265SDimitry Andric enum { CACHE_LINE = 64 };
37*81ad6265SDimitry Andric 
38*81ad6265SDimitry Andric // The Bucket class holds the struct fields we're trying to fill to a
39*81ad6265SDimitry Andric // cache-line.
40*81ad6265SDimitry Andric class Bucket {
41*81ad6265SDimitry Andric   SmallVector<FieldDecl *, 64> Fields;
42*81ad6265SDimitry Andric   int Size = 0;
43*81ad6265SDimitry Andric 
44*81ad6265SDimitry Andric public:
45*81ad6265SDimitry Andric   virtual ~Bucket() = default;
46*81ad6265SDimitry Andric 
fields()47*81ad6265SDimitry Andric   SmallVector<FieldDecl *, 64> &fields() { return Fields; }
48*81ad6265SDimitry Andric   void addField(FieldDecl *Field, int FieldSize);
canFit(int FieldSize) const49*81ad6265SDimitry Andric   virtual bool canFit(int FieldSize) const {
50*81ad6265SDimitry Andric     return Size + FieldSize <= CACHE_LINE;
51*81ad6265SDimitry Andric   }
isBitfieldRun() const52*81ad6265SDimitry Andric   virtual bool isBitfieldRun() const { return false; }
full() const53*81ad6265SDimitry Andric   bool full() const { return Size >= CACHE_LINE; }
54*81ad6265SDimitry Andric };
55*81ad6265SDimitry Andric 
addField(FieldDecl * Field,int FieldSize)56*81ad6265SDimitry Andric void Bucket::addField(FieldDecl *Field, int FieldSize) {
57*81ad6265SDimitry Andric   Size += FieldSize;
58*81ad6265SDimitry Andric   Fields.push_back(Field);
59*81ad6265SDimitry Andric }
60*81ad6265SDimitry Andric 
61*81ad6265SDimitry Andric struct BitfieldRunBucket : public Bucket {
canFit__anonce06ef4d0111::BitfieldRunBucket62*81ad6265SDimitry Andric   bool canFit(int FieldSize) const override { return true; }
isBitfieldRun__anonce06ef4d0111::BitfieldRunBucket63*81ad6265SDimitry Andric   bool isBitfieldRun() const override { return true; }
64*81ad6265SDimitry Andric };
65*81ad6265SDimitry Andric 
randomizeStructureLayoutImpl(const ASTContext & Context,llvm::SmallVectorImpl<FieldDecl * > & FieldsOut,std::mt19937 & RNG)66*81ad6265SDimitry Andric void randomizeStructureLayoutImpl(const ASTContext &Context,
67*81ad6265SDimitry Andric                                   llvm::SmallVectorImpl<FieldDecl *> &FieldsOut,
68*81ad6265SDimitry Andric                                   std::mt19937 &RNG) {
69*81ad6265SDimitry Andric   // All of the Buckets produced by best-effort cache-line algorithm.
70*81ad6265SDimitry Andric   SmallVector<std::unique_ptr<Bucket>, 16> Buckets;
71*81ad6265SDimitry Andric 
72*81ad6265SDimitry Andric   // The current bucket of fields that we are trying to fill to a cache-line.
73*81ad6265SDimitry Andric   std::unique_ptr<Bucket> CurrentBucket;
74*81ad6265SDimitry Andric 
75*81ad6265SDimitry Andric   // The current bucket containing the run of adjacent bitfields to ensure they
76*81ad6265SDimitry Andric   // remain adjacent.
77*81ad6265SDimitry Andric   std::unique_ptr<BitfieldRunBucket> CurrentBitfieldRun;
78*81ad6265SDimitry Andric 
79*81ad6265SDimitry Andric   // Tracks the number of fields that we failed to fit to the current bucket,
80*81ad6265SDimitry Andric   // and thus still need to be added later.
81*81ad6265SDimitry Andric   size_t Skipped = 0;
82*81ad6265SDimitry Andric 
83*81ad6265SDimitry Andric   while (!FieldsOut.empty()) {
84*81ad6265SDimitry Andric     // If we've Skipped more fields than we have remaining to place, that means
85*81ad6265SDimitry Andric     // that they can't fit in our current bucket, and we need to start a new
86*81ad6265SDimitry Andric     // one.
87*81ad6265SDimitry Andric     if (Skipped >= FieldsOut.size()) {
88*81ad6265SDimitry Andric       Skipped = 0;
89*81ad6265SDimitry Andric       Buckets.push_back(std::move(CurrentBucket));
90*81ad6265SDimitry Andric     }
91*81ad6265SDimitry Andric 
92*81ad6265SDimitry Andric     // Take the first field that needs to be put in a bucket.
93*81ad6265SDimitry Andric     auto FieldIter = FieldsOut.begin();
94*81ad6265SDimitry Andric     FieldDecl *FD = *FieldIter;
95*81ad6265SDimitry Andric 
96*81ad6265SDimitry Andric     if (FD->isBitField() && !FD->isZeroLengthBitField(Context)) {
97*81ad6265SDimitry Andric       // Start a bitfield run if this is the first bitfield we have found.
98*81ad6265SDimitry Andric       if (!CurrentBitfieldRun)
99*81ad6265SDimitry Andric         CurrentBitfieldRun = std::make_unique<BitfieldRunBucket>();
100*81ad6265SDimitry Andric 
101*81ad6265SDimitry Andric       // We've placed the field, and can remove it from the "awaiting Buckets"
102*81ad6265SDimitry Andric       // vector called "Fields."
103*81ad6265SDimitry Andric       CurrentBitfieldRun->addField(FD, /*FieldSize is irrelevant here*/ 1);
104*81ad6265SDimitry Andric       FieldsOut.erase(FieldIter);
105*81ad6265SDimitry Andric       continue;
106*81ad6265SDimitry Andric     }
107*81ad6265SDimitry Andric 
108*81ad6265SDimitry Andric     // Else, current field is not a bitfield. If we were previously in a
109*81ad6265SDimitry Andric     // bitfield run, end it.
110*81ad6265SDimitry Andric     if (CurrentBitfieldRun)
111*81ad6265SDimitry Andric       Buckets.push_back(std::move(CurrentBitfieldRun));
112*81ad6265SDimitry Andric 
113*81ad6265SDimitry Andric     // If we don't have a bucket, make one.
114*81ad6265SDimitry Andric     if (!CurrentBucket)
115*81ad6265SDimitry Andric       CurrentBucket = std::make_unique<Bucket>();
116*81ad6265SDimitry Andric 
117*81ad6265SDimitry Andric     uint64_t Width = Context.getTypeInfo(FD->getType()).Width;
118*81ad6265SDimitry Andric     if (Width >= CACHE_LINE) {
119*81ad6265SDimitry Andric       std::unique_ptr<Bucket> OverSized = std::make_unique<Bucket>();
120*81ad6265SDimitry Andric       OverSized->addField(FD, Width);
121*81ad6265SDimitry Andric       FieldsOut.erase(FieldIter);
122*81ad6265SDimitry Andric       Buckets.push_back(std::move(OverSized));
123*81ad6265SDimitry Andric       continue;
124*81ad6265SDimitry Andric     }
125*81ad6265SDimitry Andric 
126*81ad6265SDimitry Andric     // If it fits, add it.
127*81ad6265SDimitry Andric     if (CurrentBucket->canFit(Width)) {
128*81ad6265SDimitry Andric       CurrentBucket->addField(FD, Width);
129*81ad6265SDimitry Andric       FieldsOut.erase(FieldIter);
130*81ad6265SDimitry Andric 
131*81ad6265SDimitry Andric       // If it's now full, tie off the bucket.
132*81ad6265SDimitry Andric       if (CurrentBucket->full()) {
133*81ad6265SDimitry Andric         Skipped = 0;
134*81ad6265SDimitry Andric         Buckets.push_back(std::move(CurrentBucket));
135*81ad6265SDimitry Andric       }
136*81ad6265SDimitry Andric     } else {
137*81ad6265SDimitry Andric       // We can't fit it in our current bucket. Move to the end for processing
138*81ad6265SDimitry Andric       // later.
139*81ad6265SDimitry Andric       ++Skipped; // Mark it skipped.
140*81ad6265SDimitry Andric       FieldsOut.push_back(FD);
141*81ad6265SDimitry Andric       FieldsOut.erase(FieldIter);
142*81ad6265SDimitry Andric     }
143*81ad6265SDimitry Andric   }
144*81ad6265SDimitry Andric 
145*81ad6265SDimitry Andric   // Done processing the fields awaiting a bucket.
146*81ad6265SDimitry Andric 
147*81ad6265SDimitry Andric   // If we were filling a bucket, tie it off.
148*81ad6265SDimitry Andric   if (CurrentBucket)
149*81ad6265SDimitry Andric     Buckets.push_back(std::move(CurrentBucket));
150*81ad6265SDimitry Andric 
151*81ad6265SDimitry Andric   // If we were processing a bitfield run bucket, tie it off.
152*81ad6265SDimitry Andric   if (CurrentBitfieldRun)
153*81ad6265SDimitry Andric     Buckets.push_back(std::move(CurrentBitfieldRun));
154*81ad6265SDimitry Andric 
155*81ad6265SDimitry Andric   std::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
156*81ad6265SDimitry Andric 
157*81ad6265SDimitry Andric   // Produce the new ordering of the elements from the Buckets.
158*81ad6265SDimitry Andric   SmallVector<FieldDecl *, 16> FinalOrder;
159*81ad6265SDimitry Andric   for (const std::unique_ptr<Bucket> &B : Buckets) {
160*81ad6265SDimitry Andric     llvm::SmallVectorImpl<FieldDecl *> &RandFields = B->fields();
161*81ad6265SDimitry Andric     if (!B->isBitfieldRun())
162*81ad6265SDimitry Andric       std::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
163*81ad6265SDimitry Andric 
164*81ad6265SDimitry Andric     FinalOrder.insert(FinalOrder.end(), RandFields.begin(), RandFields.end());
165*81ad6265SDimitry Andric   }
166*81ad6265SDimitry Andric 
167*81ad6265SDimitry Andric   FieldsOut = FinalOrder;
168*81ad6265SDimitry Andric }
169*81ad6265SDimitry Andric 
170*81ad6265SDimitry Andric } // anonymous namespace
171*81ad6265SDimitry Andric 
172*81ad6265SDimitry Andric namespace clang {
173*81ad6265SDimitry Andric namespace randstruct {
174*81ad6265SDimitry Andric 
randomizeStructureLayout(const ASTContext & Context,RecordDecl * RD,SmallVectorImpl<Decl * > & FinalOrdering)175*81ad6265SDimitry Andric bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD,
176*81ad6265SDimitry Andric                               SmallVectorImpl<Decl *> &FinalOrdering) {
177*81ad6265SDimitry Andric   SmallVector<FieldDecl *, 64> RandomizedFields;
178*81ad6265SDimitry Andric   SmallVector<Decl *, 8> PostRandomizedFields;
179*81ad6265SDimitry Andric 
180*81ad6265SDimitry Andric   unsigned TotalNumFields = 0;
181*81ad6265SDimitry Andric   for (Decl *D : RD->decls()) {
182*81ad6265SDimitry Andric     ++TotalNumFields;
183*81ad6265SDimitry Andric     if (auto *FD = dyn_cast<FieldDecl>(D))
184*81ad6265SDimitry Andric       RandomizedFields.push_back(FD);
185*81ad6265SDimitry Andric     else if (isa<StaticAssertDecl>(D) || isa<IndirectFieldDecl>(D))
186*81ad6265SDimitry Andric       PostRandomizedFields.push_back(D);
187*81ad6265SDimitry Andric     else
188*81ad6265SDimitry Andric       FinalOrdering.push_back(D);
189*81ad6265SDimitry Andric   }
190*81ad6265SDimitry Andric 
191*81ad6265SDimitry Andric   if (RandomizedFields.empty())
192*81ad6265SDimitry Andric     return false;
193*81ad6265SDimitry Andric 
194*81ad6265SDimitry Andric   // Struct might end with a flexible array or an array of size 0 or 1,
195*81ad6265SDimitry Andric   // in which case we don't want to randomize it.
196*81ad6265SDimitry Andric   FieldDecl *FlexibleArray =
197*81ad6265SDimitry Andric       RD->hasFlexibleArrayMember() ? RandomizedFields.pop_back_val() : nullptr;
198*81ad6265SDimitry Andric   if (!FlexibleArray) {
199*81ad6265SDimitry Andric     if (const auto *CA =
200*81ad6265SDimitry Andric             dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType()))
201*81ad6265SDimitry Andric       if (CA->getSize().sle(2))
202*81ad6265SDimitry Andric         FlexibleArray = RandomizedFields.pop_back_val();
203*81ad6265SDimitry Andric   }
204*81ad6265SDimitry Andric 
205*81ad6265SDimitry Andric   std::string Seed =
206*81ad6265SDimitry Andric       Context.getLangOpts().RandstructSeed + RD->getNameAsString();
207*81ad6265SDimitry Andric   std::seed_seq SeedSeq(Seed.begin(), Seed.end());
208*81ad6265SDimitry Andric   std::mt19937 RNG(SeedSeq);
209*81ad6265SDimitry Andric 
210*81ad6265SDimitry Andric   randomizeStructureLayoutImpl(Context, RandomizedFields, RNG);
211*81ad6265SDimitry Andric 
212*81ad6265SDimitry Andric   // Plorp the randomized decls into the final ordering.
213*81ad6265SDimitry Andric   FinalOrdering.insert(FinalOrdering.end(), RandomizedFields.begin(),
214*81ad6265SDimitry Andric                        RandomizedFields.end());
215*81ad6265SDimitry Andric 
216*81ad6265SDimitry Andric   // Add fields that belong towards the end of the RecordDecl.
217*81ad6265SDimitry Andric   FinalOrdering.insert(FinalOrdering.end(), PostRandomizedFields.begin(),
218*81ad6265SDimitry Andric                        PostRandomizedFields.end());
219*81ad6265SDimitry Andric 
220*81ad6265SDimitry Andric   // Add back the flexible array.
221*81ad6265SDimitry Andric   if (FlexibleArray)
222*81ad6265SDimitry Andric     FinalOrdering.push_back(FlexibleArray);
223*81ad6265SDimitry Andric 
224*81ad6265SDimitry Andric   assert(TotalNumFields == FinalOrdering.size() &&
225*81ad6265SDimitry Andric          "Decl count has been altered after Randstruct randomization!");
226*81ad6265SDimitry Andric   (void)TotalNumFields;
227*81ad6265SDimitry Andric   return true;
228*81ad6265SDimitry Andric }
229*81ad6265SDimitry Andric 
230*81ad6265SDimitry Andric } // end namespace randstruct
231*81ad6265SDimitry Andric } // end namespace clang
232