#include "clang/AST/Randstruct.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <random>
#include <set>
#include <sstream>
#include <string>
using clang::ASTContext;
using clang::FieldDecl;
using llvm::SmallVector;
namespace {
enum { CACHE_LINE = 64 };
class Bucket {
SmallVector<FieldDecl *, 64> Fields;
int Size = 0;
public:
virtual ~Bucket() = default;
SmallVector<FieldDecl *, 64> &fields() { return Fields; }
void addField(FieldDecl *Field, int FieldSize);
virtual bool canFit(int FieldSize) const {
return Size + FieldSize <= CACHE_LINE;
}
virtual bool isBitfieldRun() const { return false; }
bool full() const { return Size >= CACHE_LINE; }
};
void Bucket::addField(FieldDecl *Field, int FieldSize) {
Size += FieldSize;
Fields.push_back(Field);
}
struct BitfieldRunBucket : public Bucket {
bool canFit(int FieldSize) const override { return true; }
bool isBitfieldRun() const override { return true; }
};
void randomizeStructureLayoutImpl(const ASTContext &Context,
llvm::SmallVectorImpl<FieldDecl *> &FieldsOut,
std::mt19937 &RNG) {
SmallVector<std::unique_ptr<Bucket>, 16> Buckets;
std::unique_ptr<Bucket> CurrentBucket;
std::unique_ptr<BitfieldRunBucket> CurrentBitfieldRun;
size_t Skipped = 0;
while (!FieldsOut.empty()) {
if (Skipped >= FieldsOut.size()) {
Skipped = 0;
Buckets.push_back(std::move(CurrentBucket));
}
auto FieldIter = FieldsOut.begin();
FieldDecl *FD = *FieldIter;
if (FD->isBitField() && !FD->isZeroLengthBitField(Context)) {
if (!CurrentBitfieldRun)
CurrentBitfieldRun = std::make_unique<BitfieldRunBucket>();
CurrentBitfieldRun->addField(FD, 1);
FieldsOut.erase(FieldIter);
continue;
}
if (CurrentBitfieldRun)
Buckets.push_back(std::move(CurrentBitfieldRun));
if (!CurrentBucket)
CurrentBucket = std::make_unique<Bucket>();
uint64_t Width = Context.getTypeInfo(FD->getType()).Width;
if (Width >= CACHE_LINE) {
std::unique_ptr<Bucket> OverSized = std::make_unique<Bucket>();
OverSized->addField(FD, Width);
FieldsOut.erase(FieldIter);
Buckets.push_back(std::move(OverSized));
continue;
}
if (CurrentBucket->canFit(Width)) {
CurrentBucket->addField(FD, Width);
FieldsOut.erase(FieldIter);
if (CurrentBucket->full()) {
Skipped = 0;
Buckets.push_back(std::move(CurrentBucket));
}
} else {
++Skipped; FieldsOut.push_back(FD);
FieldsOut.erase(FieldIter);
}
}
if (CurrentBucket)
Buckets.push_back(std::move(CurrentBucket));
if (CurrentBitfieldRun)
Buckets.push_back(std::move(CurrentBitfieldRun));
std::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
SmallVector<FieldDecl *, 16> FinalOrder;
for (const std::unique_ptr<Bucket> &B : Buckets) {
llvm::SmallVectorImpl<FieldDecl *> &RandFields = B->fields();
if (!B->isBitfieldRun())
std::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
FinalOrder.insert(FinalOrder.end(), RandFields.begin(), RandFields.end());
}
FieldsOut = FinalOrder;
}
}
namespace clang {
namespace randstruct {
bool randomizeStructureLayout(const ASTContext &Context, RecordDecl *RD,
SmallVectorImpl<Decl *> &FinalOrdering) {
SmallVector<FieldDecl *, 64> RandomizedFields;
SmallVector<Decl *, 8> PostRandomizedFields;
unsigned TotalNumFields = 0;
for (Decl *D : RD->decls()) {
++TotalNumFields;
if (auto *FD = dyn_cast<FieldDecl>(D))
RandomizedFields.push_back(FD);
else if (isa<StaticAssertDecl>(D) || isa<IndirectFieldDecl>(D))
PostRandomizedFields.push_back(D);
else
FinalOrdering.push_back(D);
}
if (RandomizedFields.empty())
return false;
FieldDecl *FlexibleArray =
RD->hasFlexibleArrayMember() ? RandomizedFields.pop_back_val() : nullptr;
if (!FlexibleArray) {
if (const auto *CA =
dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType()))
if (CA->getSize().sle(2))
FlexibleArray = RandomizedFields.pop_back_val();
}
std::string Seed =
Context.getLangOpts().RandstructSeed + RD->getNameAsString();
std::seed_seq SeedSeq(Seed.begin(), Seed.end());
std::mt19937 RNG(SeedSeq);
randomizeStructureLayoutImpl(Context, RandomizedFields, RNG);
FinalOrdering.insert(FinalOrdering.end(), RandomizedFields.begin(),
RandomizedFields.end());
FinalOrdering.insert(FinalOrdering.end(), PostRandomizedFields.begin(),
PostRandomizedFields.end());
if (FlexibleArray)
FinalOrdering.push_back(FlexibleArray);
assert(TotalNumFields == FinalOrdering.size() &&
"Decl count has been altered after Randstruct randomization!");
(void)TotalNumFields;
return true;
}
} }