forked from OSchip/llvm-project
Revert D121556 "[randstruct] Add randomize structure layout support"
This reverts commit 3f0587d0c6
.
Not all tests pass after a few rounds of fixes.
I spot one failure that std::shuffle (potentially different results with
different STL implementations) was misused and replaced it with llvm::shuffle,
but there appears to be another failure in a Windows build.
The latest failure is reported on https://reviews.llvm.org/D121556#3440383
This commit is contained in:
parent
62c8b185ff
commit
a58d0af058
|
@ -54,20 +54,6 @@ Major New Features
|
|||
There is an analogous ``zero_call_used_regs`` attribute to allow for finer
|
||||
control of this feature.
|
||||
|
||||
- Clang now supports randomizing structure layout in C. This feature is a
|
||||
compile-time hardening technique, making it more difficult for an attacker to
|
||||
retrieve data from structures. Specify randomization with the
|
||||
``randomize_layout`` attribute. The corresponding ``no_randomize_layout``
|
||||
attribute can be used to turn the feature off.
|
||||
|
||||
A seed value is required to enable randomization, and is deterministic based
|
||||
on a seed value. Use the ``-frandomize-layout-seed=`` or
|
||||
``-frandomize-layout-seed-file=`` flags.
|
||||
|
||||
.. note::
|
||||
|
||||
Randomizing structure layout is a C-only feature.
|
||||
|
||||
Bug Fixes
|
||||
------------------
|
||||
- ``CXXNewExpr::getArraySize()`` previously returned a ``llvm::Optional``
|
||||
|
|
|
@ -4051,12 +4051,6 @@ public:
|
|||
RecordDeclBits.ParamDestroyedInCallee = V;
|
||||
}
|
||||
|
||||
bool isRandomized() const { return RecordDeclBits.IsRandomized; }
|
||||
|
||||
void setIsRandomized(bool V) { RecordDeclBits.IsRandomized = V; }
|
||||
|
||||
void reorderFields(const SmallVectorImpl<Decl *> &Fields);
|
||||
|
||||
/// Determines whether this declaration represents the
|
||||
/// injected class name.
|
||||
///
|
||||
|
|
|
@ -313,7 +313,6 @@ protected:
|
|||
friend class ASTReader;
|
||||
friend class CXXClassMemberWrapper;
|
||||
friend class LinkageComputer;
|
||||
friend class RecordDecl;
|
||||
template<typename decl_type> friend class Redeclarable;
|
||||
|
||||
/// Access - Used by C++ decls for the access specifier.
|
||||
|
@ -1541,13 +1540,10 @@ class DeclContext {
|
|||
|
||||
/// Represents the way this type is passed to a function.
|
||||
uint64_t ArgPassingRestrictions : 2;
|
||||
|
||||
/// Indicates whether this struct has had its field layout randomized.
|
||||
uint64_t IsRandomized : 1;
|
||||
};
|
||||
|
||||
/// Number of non-inherited bits in RecordDeclBitfields.
|
||||
enum { NumRecordDeclBits = 15 };
|
||||
enum { NumRecordDeclBits = 14 };
|
||||
|
||||
/// Stores the bits used by OMPDeclareReductionDecl.
|
||||
/// If modified NumOMPDeclareReductionDeclBits and the accessor
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
//===- Randstruct.h - Interfact for structure randomization -------*- C++ -*-=//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the interface for Clang's structure field layout
|
||||
// randomization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_RANDSTRUCT_H
|
||||
#define LLVM_CLANG_AST_RANDSTRUCT_H
|
||||
|
||||
namespace llvm {
|
||||
template <typename T> class ArrayRef;
|
||||
template <typename T> class SmallVectorImpl;
|
||||
class StringRef;
|
||||
} // end namespace llvm
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class Decl;
|
||||
class RecordDecl;
|
||||
|
||||
namespace randstruct {
|
||||
|
||||
bool randomizeStructureLayout(const ASTContext &Context, llvm::StringRef Name,
|
||||
llvm::ArrayRef<Decl *> Fields,
|
||||
llvm::SmallVectorImpl<Decl *> &FinalOrdering);
|
||||
|
||||
} // namespace randstruct
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_RANDSTRUCT_H
|
|
@ -3957,18 +3957,3 @@ def HLSLNumThreads: InheritableAttr {
|
|||
let LangOpts = [HLSL];
|
||||
let Documentation = [NumThreadsDocs];
|
||||
}
|
||||
|
||||
def RandomizeLayout : InheritableAttr {
|
||||
let Spellings = [GCC<"randomize_layout">];
|
||||
let Subjects = SubjectList<[Record]>;
|
||||
let Documentation = [ClangRandomizeLayoutDocs];
|
||||
let LangOpts = [COnly];
|
||||
}
|
||||
|
||||
def NoRandomizeLayout : InheritableAttr {
|
||||
let Spellings = [GCC<"no_randomize_layout">];
|
||||
let Subjects = SubjectList<[Record]>;
|
||||
let Documentation = [ClangRandomizeLayoutDocs];
|
||||
let LangOpts = [COnly];
|
||||
}
|
||||
def : MutualExclusions<[RandomizeLayout, NoRandomizeLayout]>;
|
||||
|
|
|
@ -6379,35 +6379,3 @@ dictate the thread id. Total number of threads executed is ``X * Y * Z``.
|
|||
The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-numthreads
|
||||
}];
|
||||
}
|
||||
|
||||
def ClangRandomizeLayoutDocs : Documentation {
|
||||
let Category = DocCatDecl;
|
||||
let Heading = "randomize_layout, no_randomize_layout";
|
||||
let Content = [{
|
||||
The attribute ``randomize_layout``, when attached to a C structure, selects it
|
||||
for structure layout field randomization; a compile-time hardening technique. A
|
||||
"seed" value, is specified via the ``-frandomize-layout-seed=`` command line flag.
|
||||
For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'`
|
||||
make ... CFLAGS="-frandomize-layout-seed=$SEED" ...
|
||||
|
||||
You can also supply the seed in a file with ``-frandomize-layout-seed-file=``.
|
||||
For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n' > /tmp/seed_file.txt
|
||||
make ... CFLAGS="-frandomize-layout-seed-file=/tmp/seed_file.txt" ...
|
||||
|
||||
The randomization is deterministic based for a given seed, so the entire
|
||||
program should be compiled with the same seed, but keep the seed safe
|
||||
otherwise.
|
||||
|
||||
The attribute ``no_randomize_layout``, when attached to a C structure,
|
||||
instructs the compiler that this structure should not have its field layout
|
||||
randomized.
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -165,8 +165,6 @@ def err_drv_amdgpu_ieee_without_no_honor_nans : Error<
|
|||
"invalid argument '-mno-amdgpu-ieee' only allowed with relaxed NaN handling">;
|
||||
def err_drv_argument_not_allowed_with : Error<
|
||||
"invalid argument '%0' not allowed with '%1'">;
|
||||
def err_drv_cannot_open_randomize_layout_seed_file : Error<
|
||||
"cannot read randomize layout seed file '%0'">;
|
||||
def err_drv_invalid_version_number : Error<
|
||||
"invalid version number in '%0'">;
|
||||
def err_drv_no_linker_llvm_support : Error<
|
||||
|
|
|
@ -11588,7 +11588,5 @@ def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numt
|
|||
def err_hlsl_numthreads_invalid : Error<"total number of threads cannot exceed %0">;
|
||||
def err_hlsl_attribute_param_mismatch : Error<"%0 attribute parameters do not match the previous declaration">;
|
||||
|
||||
// Layout randomization warning.
|
||||
def err_cast_from_randomized_struct : Error<
|
||||
"casting from randomized structure pointer type %0 to %1">;
|
||||
} // end of sema component.
|
||||
|
||||
|
|
|
@ -445,9 +445,6 @@ public:
|
|||
/// The default stream kind used for HIP kernel launching.
|
||||
GPUDefaultStreamKind GPUDefaultStream;
|
||||
|
||||
/// The seed used by the randomize structure layout feature.
|
||||
std::string RandstructSeed;
|
||||
|
||||
LangOptions();
|
||||
|
||||
// Define accessors/mutators for language options of enumeration type.
|
||||
|
|
|
@ -2122,12 +2122,6 @@ defm merge_all_constants : BoolFOption<"merge-all-constants",
|
|||
def fmessage_length_EQ : Joined<["-"], "fmessage-length=">, Group<f_Group>, Flags<[CC1Option]>,
|
||||
HelpText<"Format message diagnostics so that they fit within N columns">,
|
||||
MarshallingInfoInt<DiagnosticOpts<"MessageLength">>;
|
||||
def frandomize_layout_seed_EQ : Joined<["-"], "frandomize-layout-seed=">,
|
||||
MetaVarName<"<seed>">, Group<f_clang_Group>, Flags<[CC1Option]>,
|
||||
HelpText<"The seed used by the randomize structure layout feature">;
|
||||
def frandomize_layout_seed_file_EQ : Joined<["-"], "frandomize-layout-seed-file=">,
|
||||
MetaVarName<"<file>">, Group<f_clang_Group>, Flags<[CC1Option]>,
|
||||
HelpText<"File holding the seed used by the randomize structure layout feature">;
|
||||
def fms_compatibility : Flag<["-"], "fms-compatibility">, Group<f_Group>, Flags<[CC1Option, CoreOption]>,
|
||||
HelpText<"Enable full Microsoft Visual C++ compatibility">,
|
||||
MarshallingInfoFlag<LangOpts<"MSVCCompat">>;
|
||||
|
|
|
@ -97,7 +97,6 @@ add_clang_library(clangAST
|
|||
ParentMap.cpp
|
||||
PrintfFormatString.cpp
|
||||
QualTypeNames.cpp
|
||||
Randstruct.cpp
|
||||
RawCommentList.cpp
|
||||
RecordLayout.cpp
|
||||
RecordLayoutBuilder.cpp
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "clang/AST/ODRHash.h"
|
||||
#include "clang/AST/PrettyDeclStackTrace.h"
|
||||
#include "clang/AST/PrettyPrinter.h"
|
||||
#include "clang/AST/Randstruct.h"
|
||||
#include "clang/AST/Redeclarable.h"
|
||||
#include "clang/AST/Stmt.h"
|
||||
#include "clang/AST/TemplateBase.h"
|
||||
|
@ -4584,7 +4583,6 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, const ASTContext &C,
|
|||
setHasNonTrivialToPrimitiveCopyCUnion(false);
|
||||
setParamDestroyedInCallee(false);
|
||||
setArgPassingRestrictions(APK_CanPassInRegs);
|
||||
setIsRandomized(false);
|
||||
}
|
||||
|
||||
RecordDecl *RecordDecl::Create(const ASTContext &C, TagKind TK, DeclContext *DC,
|
||||
|
@ -4668,12 +4666,6 @@ bool RecordDecl::isMsStruct(const ASTContext &C) const {
|
|||
return hasAttr<MSStructAttr>() || C.getLangOpts().MSBitfields == 1;
|
||||
}
|
||||
|
||||
void RecordDecl::reorderFields(const SmallVectorImpl<Decl *> &Fields) {
|
||||
std::tie(FirstDecl, LastDecl) = DeclContext::BuildDeclChain(Fields, false);
|
||||
LastDecl->NextInContextAndBits.setPointer(nullptr);
|
||||
setIsRandomized(true);
|
||||
}
|
||||
|
||||
void RecordDecl::LoadFieldsFromExternalStorage() const {
|
||||
ExternalASTSource *Source = getASTContext().getExternalSource();
|
||||
assert(hasExternalLexicalStorage() && Source && "No external storage?");
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
//===--- Randstruct.cpp ---------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the implementation for Clang's structure field layout
|
||||
// randomization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/Randstruct.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ASTDiagnostic.h"
|
||||
#include "clang/AST/Attr.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 {
|
||||
|
||||
// FIXME: Replace this with some discovery once that mechanism exists.
|
||||
enum { CACHE_LINE = 64 };
|
||||
|
||||
// The Bucket class holds the struct fields we're trying to fill to a
|
||||
// cache-line.
|
||||
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) {
|
||||
// All of the Buckets produced by best-effort cache-line algorithm.
|
||||
SmallVector<std::unique_ptr<Bucket>, 16> Buckets;
|
||||
|
||||
// The current bucket of fields that we are trying to fill to a cache-line.
|
||||
std::unique_ptr<Bucket> CurrentBucket;
|
||||
|
||||
// The current bucket containing the run of adjacent bitfields to ensure they
|
||||
// remain adjacent.
|
||||
std::unique_ptr<BitfieldRunBucket> CurrentBitfieldRun;
|
||||
|
||||
// Tracks the number of fields that we failed to fit to the current bucket,
|
||||
// and thus still need to be added later.
|
||||
size_t Skipped = 0;
|
||||
|
||||
while (!FieldsOut.empty()) {
|
||||
// If we've Skipped more fields than we have remaining to place, that means
|
||||
// that they can't fit in our current bucket, and we need to start a new
|
||||
// one.
|
||||
if (Skipped >= FieldsOut.size()) {
|
||||
Skipped = 0;
|
||||
Buckets.push_back(std::move(CurrentBucket));
|
||||
}
|
||||
|
||||
// Take the first field that needs to be put in a bucket.
|
||||
auto FieldIter = FieldsOut.begin();
|
||||
FieldDecl *FD = *FieldIter;
|
||||
|
||||
if (FD->isBitField() && !FD->isZeroLengthBitField(Context)) {
|
||||
// Start a bitfield run if this is the first bitfield we have found.
|
||||
if (!CurrentBitfieldRun)
|
||||
CurrentBitfieldRun = std::make_unique<BitfieldRunBucket>();
|
||||
|
||||
// We've placed the field, and can remove it from the "awaiting Buckets"
|
||||
// vector called "Fields."
|
||||
CurrentBitfieldRun->addField(FD, /*FieldSize is irrelevant here*/ 1);
|
||||
FieldsOut.erase(FieldIter);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Else, current field is not a bitfield. If we were previously in a
|
||||
// bitfield run, end it.
|
||||
if (CurrentBitfieldRun)
|
||||
Buckets.push_back(std::move(CurrentBitfieldRun));
|
||||
|
||||
// If we don't have a bucket, make one.
|
||||
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 it fits, add it.
|
||||
if (CurrentBucket->canFit(Width)) {
|
||||
CurrentBucket->addField(FD, Width);
|
||||
FieldsOut.erase(FieldIter);
|
||||
|
||||
// If it's now full, tie off the bucket.
|
||||
if (CurrentBucket->full()) {
|
||||
Skipped = 0;
|
||||
Buckets.push_back(std::move(CurrentBucket));
|
||||
}
|
||||
} else {
|
||||
// We can't fit it in our current bucket. Move to the end for processing
|
||||
// later.
|
||||
++Skipped; // Mark it skipped.
|
||||
FieldsOut.push_back(FD);
|
||||
FieldsOut.erase(FieldIter);
|
||||
}
|
||||
}
|
||||
|
||||
// Done processing the fields awaiting a bucket.
|
||||
|
||||
// If we were filling a bucket, tie it off.
|
||||
if (CurrentBucket)
|
||||
Buckets.push_back(std::move(CurrentBucket));
|
||||
|
||||
// If we were processing a bitfield run bucket, tie it off.
|
||||
if (CurrentBitfieldRun)
|
||||
Buckets.push_back(std::move(CurrentBitfieldRun));
|
||||
|
||||
llvm::shuffle(std::begin(Buckets), std::end(Buckets), RNG);
|
||||
|
||||
// Produce the new ordering of the elements from the Buckets.
|
||||
SmallVector<FieldDecl *, 16> FinalOrder;
|
||||
for (const std::unique_ptr<Bucket> &B : Buckets) {
|
||||
llvm::SmallVectorImpl<FieldDecl *> &RandFields = B->fields();
|
||||
if (!B->isBitfieldRun())
|
||||
llvm::shuffle(std::begin(RandFields), std::end(RandFields), RNG);
|
||||
|
||||
FinalOrder.insert(FinalOrder.end(), RandFields.begin(), RandFields.end());
|
||||
}
|
||||
|
||||
FieldsOut = FinalOrder;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace clang {
|
||||
namespace randstruct {
|
||||
|
||||
bool randomizeStructureLayout(const ASTContext &Context, StringRef Name,
|
||||
ArrayRef<Decl *> Fields,
|
||||
SmallVectorImpl<Decl *> &FinalOrdering) {
|
||||
SmallVector<FieldDecl *, 64> RandomizedFields;
|
||||
|
||||
unsigned TotalNumFields = 0;
|
||||
for (Decl *D : Fields) {
|
||||
++TotalNumFields;
|
||||
if (auto *FD = dyn_cast<FieldDecl>(D))
|
||||
RandomizedFields.push_back(FD);
|
||||
else
|
||||
FinalOrdering.push_back(D);
|
||||
}
|
||||
|
||||
if (RandomizedFields.empty())
|
||||
return false;
|
||||
|
||||
// Struct might end with a variable-length array or an array of size 0 or 1,
|
||||
// in which case we don't want to randomize it.
|
||||
FieldDecl *VLA = nullptr;
|
||||
const auto *CA =
|
||||
dyn_cast<ConstantArrayType>(RandomizedFields.back()->getType());
|
||||
if ((CA && (CA->getSize().sle(2) || CA->isIncompleteArrayType())) ||
|
||||
llvm::any_of(Fields, [](Decl *D) {
|
||||
if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
|
||||
const Type *FDTy = FD->getType().getTypePtr();
|
||||
if (const RecordType *FDTTy = FDTy->getAs<RecordType>())
|
||||
return FDTTy->getDecl()->hasFlexibleArrayMember();
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
VLA = RandomizedFields.pop_back_val();
|
||||
|
||||
std::string Seed = (Context.getLangOpts().RandstructSeed + Name).str();
|
||||
std::seed_seq SeedSeq(Seed.begin(), Seed.end());
|
||||
std::mt19937 RNG(SeedSeq);
|
||||
|
||||
randomizeStructureLayoutImpl(Context, RandomizedFields, RNG);
|
||||
if (VLA)
|
||||
RandomizedFields.push_back(VLA);
|
||||
|
||||
FinalOrdering.insert(FinalOrdering.end(), RandomizedFields.begin(),
|
||||
RandomizedFields.end());
|
||||
|
||||
assert(TotalNumFields == FinalOrdering.size() &&
|
||||
"Decl count has been altered after Randstruct randomization!");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace randstruct
|
||||
} // end namespace clang
|
|
@ -5907,14 +5907,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
|
|||
CmdArgs.push_back(
|
||||
Args.MakeArgString("-fmessage-length=" + Twine(MessageLength)));
|
||||
|
||||
if (Arg *A = Args.getLastArg(options::OPT_frandomize_layout_seed_EQ))
|
||||
CmdArgs.push_back(
|
||||
Args.MakeArgString("-frandomize-layout-seed=" + Twine(A->getValue(0))));
|
||||
|
||||
if (Arg *A = Args.getLastArg(options::OPT_frandomize_layout_seed_file_EQ))
|
||||
CmdArgs.push_back(Args.MakeArgString("-frandomize-layout-seed-file=" +
|
||||
Twine(A->getValue(0))));
|
||||
|
||||
// -fvisibility= and -fvisibility-ms-compat are of a piece.
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_fvisibility_EQ,
|
||||
options::OPT_fvisibility_ms_compat)) {
|
||||
|
|
|
@ -94,7 +94,6 @@
|
|||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
@ -3686,9 +3685,6 @@ void CompilerInvocation::GenerateLangArgs(const LangOptions &Opts,
|
|||
|
||||
for (const auto &MP : Opts.MacroPrefixMap)
|
||||
GenerateArg(Args, OPT_fmacro_prefix_map_EQ, MP.first + "=" + MP.second, SA);
|
||||
|
||||
if (!Opts.RandstructSeed.empty())
|
||||
GenerateArg(Args, OPT_frandomize_layout_seed_EQ, Opts.RandstructSeed, SA);
|
||||
}
|
||||
|
||||
bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
|
@ -4241,19 +4237,6 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
|||
Diags.Report(diag::err_cc1_unbounded_vscale_min);
|
||||
}
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_frandomize_layout_seed_file_EQ)) {
|
||||
std::ifstream SeedFile(A->getValue(0));
|
||||
|
||||
if (!SeedFile.is_open())
|
||||
Diags.Report(diag::err_drv_cannot_open_randomize_layout_seed_file)
|
||||
<< A->getValue(0);
|
||||
|
||||
std::getline(SeedFile, Opts.RandstructSeed);
|
||||
}
|
||||
|
||||
if (const Arg *A = Args.getLastArg(OPT_frandomize_layout_seed_EQ))
|
||||
Opts.RandstructSeed = A->getValue(0);
|
||||
|
||||
return Diags.getNumErrors() == NumErrorsBefore;
|
||||
}
|
||||
|
||||
|
|
|
@ -3129,23 +3129,6 @@ void CastOperation::CheckCStyleCast() {
|
|||
Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
|
||||
<< SrcType << DestType << OpRange;
|
||||
|
||||
if (isa<PointerType>(SrcType) && isa<PointerType>(DestType)) {
|
||||
QualType SrcTy = cast<PointerType>(SrcType)->getPointeeType();
|
||||
QualType DestTy = cast<PointerType>(DestType)->getPointeeType();
|
||||
|
||||
const RecordDecl *SrcRD = SrcTy->getAsRecordDecl();
|
||||
const RecordDecl *DestRD = DestTy->getAsRecordDecl();
|
||||
|
||||
if (SrcRD && DestRD && SrcRD->hasAttr<RandomizeLayoutAttr>() &&
|
||||
SrcRD != DestRD) {
|
||||
// The struct we are casting the pointer from was randomized.
|
||||
Self.Diag(OpRange.getBegin(), diag::err_cast_from_randomized_struct)
|
||||
<< SrcType << DestType;
|
||||
SrcExpr = ExprError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
|
||||
DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange);
|
||||
DiagnoseBadFunctionCast(Self, SrcExpr, DestType);
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
#include "clang/AST/NonTrivialTypeVisitor.h"
|
||||
#include "clang/AST/Randstruct.h"
|
||||
#include "clang/AST/StmtCXX.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
|
@ -17969,18 +17968,6 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
|
|||
// Handle attributes before checking the layout.
|
||||
ProcessDeclAttributeList(S, Record, Attrs);
|
||||
|
||||
// Maybe randomize the field order.
|
||||
if (!getLangOpts().CPlusPlus && Record->hasAttr<RandomizeLayoutAttr>() &&
|
||||
!Record->isUnion() && !getLangOpts().RandstructSeed.empty() &&
|
||||
!Record->isRandomized()) {
|
||||
SmallVector<Decl *, 32> OrigFieldOrdering(Record->fields());
|
||||
SmallVector<Decl *, 32> NewFieldOrdering;
|
||||
if (randstruct::randomizeStructureLayout(
|
||||
Context, Record->getNameAsString(), OrigFieldOrdering,
|
||||
NewFieldOrdering))
|
||||
Record->reorderFields(NewFieldOrdering);
|
||||
}
|
||||
|
||||
// We may have deferred checking for a deleted destructor. Check now.
|
||||
if (CXXRecord) {
|
||||
auto *Dtor = CXXRecord->getDestructor();
|
||||
|
|
|
@ -5121,21 +5121,6 @@ static void handleLifetimeCategoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
|||
}
|
||||
}
|
||||
|
||||
static void handleRandomizeLayoutAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
|
||||
if (checkAttrMutualExclusion<NoRandomizeLayoutAttr>(S, D, AL))
|
||||
return;
|
||||
if (!D->hasAttr<RandomizeLayoutAttr>())
|
||||
D->addAttr(::new (S.Context) RandomizeLayoutAttr(S.Context, AL));
|
||||
}
|
||||
|
||||
static void handleNoRandomizeLayoutAttr(Sema &S, Decl *D,
|
||||
const ParsedAttr &AL) {
|
||||
if (checkAttrMutualExclusion<RandomizeLayoutAttr>(S, D, AL))
|
||||
return;
|
||||
if (!D->hasAttr<NoRandomizeLayoutAttr>())
|
||||
D->addAttr(::new (S.Context) NoRandomizeLayoutAttr(S.Context, AL));
|
||||
}
|
||||
|
||||
bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
|
||||
const FunctionDecl *FD) {
|
||||
if (Attrs.isInvalid())
|
||||
|
@ -8650,12 +8635,6 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
case ParsedAttr::AT_Section:
|
||||
handleSectionAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_RandomizeLayout:
|
||||
handleRandomizeLayoutAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_NoRandomizeLayout:
|
||||
handleNoRandomizeLayoutAttr(S, D, AL);
|
||||
break;
|
||||
case ParsedAttr::AT_CodeSeg:
|
||||
handleCodeSegAttr(S, D, AL);
|
||||
break;
|
||||
|
|
|
@ -104,7 +104,6 @@
|
|||
// CHECK-NEXT: NoMicroMips (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NoMips16 (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NoProfileFunction (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: NoRandomizeLayout (SubjectMatchRule_record)
|
||||
// CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global)
|
||||
// CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global)
|
||||
// CHECK-NEXT: NoSpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
|
||||
|
@ -149,7 +148,6 @@
|
|||
// CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
|
||||
// CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method)
|
||||
// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
|
||||
// CHECK-NEXT: RandomizeLayout (SubjectMatchRule_record)
|
||||
// CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter)
|
||||
// CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
|
||||
// CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
|
||||
|
|
|
@ -25,7 +25,6 @@ add_clang_unittest(ASTTests
|
|||
EvaluateAsRValueTest.cpp
|
||||
ExternalASTSourceTest.cpp
|
||||
NamedDeclPrinterTest.cpp
|
||||
RandstructTest.cpp
|
||||
RecursiveASTVisitorTest.cpp
|
||||
SizelessTypesTest.cpp
|
||||
SourceLocationTest.cpp
|
||||
|
|
|
@ -1,442 +0,0 @@
|
|||
//===- unittest/AST/RandstructTest.cpp ------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains tests for Clang's structure field layout randomization.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/*
|
||||
* Build this test suite by running `make ASTTests` in the build folder.
|
||||
*
|
||||
* Run this test suite by running the following in the build folder:
|
||||
* ` ./tools/clang/unittests/AST/ASTTests
|
||||
* --gtest_filter=StructureLayoutRandomization*`
|
||||
*/
|
||||
|
||||
#include "clang/AST/Randstruct.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "DeclMatcher.h"
|
||||
#include "clang/AST/RecordLayout.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/Frontend/ASTUnit.h"
|
||||
#include "clang/Testing/CommandLineArgs.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang::randstruct;
|
||||
|
||||
using field_names = std::vector<std::string>;
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<ASTUnit> makeAST(const std::string &SourceCode) {
|
||||
std::vector<std::string> Args = getCommandLineArgsForTesting(Lang_C99);
|
||||
Args.push_back("-frandomize-layout-seed=1234567890abcdef");
|
||||
|
||||
IgnoringDiagConsumer IgnoringConsumer = IgnoringDiagConsumer();
|
||||
|
||||
return tooling::buildASTFromCodeWithArgs(
|
||||
SourceCode, Args, "input.c", "clang-tool",
|
||||
std::make_shared<PCHContainerOperations>(),
|
||||
tooling::getClangStripDependencyFileAdjuster(),
|
||||
tooling::FileContentMappings(), &IgnoringConsumer);
|
||||
}
|
||||
|
||||
RecordDecl *getRecordDeclFromAST(const ASTContext &C, const std::string &Name) {
|
||||
RecordDecl *RD = FirstDeclMatcher<RecordDecl>().match(
|
||||
C.getTranslationUnitDecl(), recordDecl(hasName(Name)));
|
||||
return RD;
|
||||
}
|
||||
|
||||
std::vector<std::string> getFieldNamesFromRecord(const RecordDecl *RD) {
|
||||
std::vector<std::string> Fields;
|
||||
|
||||
Fields.reserve(8);
|
||||
for (auto *Field : RD->fields())
|
||||
Fields.push_back(Field->getNameAsString());
|
||||
|
||||
return Fields;
|
||||
}
|
||||
|
||||
bool isSubsequence(const field_names &Seq, const field_names &Subseq) {
|
||||
unsigned SeqLen = Seq.size();
|
||||
unsigned SubLen = Subseq.size();
|
||||
|
||||
bool IsSubseq = false;
|
||||
for (unsigned I = 0; I < SeqLen; ++I)
|
||||
if (Seq[I] == Subseq[0]) {
|
||||
IsSubseq = true;
|
||||
for (unsigned J = 0; J + I < SeqLen && J < SubLen; ++J) {
|
||||
if (Seq[J + I] != Subseq[J]) {
|
||||
IsSubseq = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IsSubseq;
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace clang {
|
||||
namespace ast_matchers {
|
||||
|
||||
#define RANDSTRUCT_TEST_SUITE_TEST StructureLayoutRandomizationTestSuiteTest
|
||||
|
||||
TEST(RANDSTRUCT_TEST_SUITE_TEST, CanDetermineIfSubsequenceExists) {
|
||||
const field_names Seq = {"a", "b", "c", "d"};
|
||||
|
||||
EXPECT_TRUE(isSubsequence(Seq, {"b", "c"}));
|
||||
EXPECT_TRUE(isSubsequence(Seq, {"a", "b", "c", "d"}));
|
||||
EXPECT_TRUE(isSubsequence(Seq, {"b", "c", "d"}));
|
||||
EXPECT_TRUE(isSubsequence(Seq, {"a"}));
|
||||
EXPECT_FALSE(isSubsequence(Seq, {"a", "d"}));
|
||||
}
|
||||
|
||||
#define RANDSTRUCT_TEST StructureLayoutRandomization
|
||||
|
||||
TEST(RANDSTRUCT_TEST, UnmarkedStruct) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
};
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
||||
const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
|
||||
|
||||
EXPECT_FALSE(RD->hasAttr<RandomizeLayoutAttr>());
|
||||
EXPECT_FALSE(RD->isRandomized());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, MarkedNoRandomize) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
} __attribute__((no_randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
||||
const field_names Expected = {"bacon", "lettuce", "tomato", "mayonnaise"};
|
||||
|
||||
EXPECT_TRUE(RD->hasAttr<NoRandomizeLayoutAttr>());
|
||||
EXPECT_FALSE(RD->isRandomized());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, MarkedRandomize) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
||||
const field_names Expected = {"lettuce", "bacon", "mayonnaise", "tomato"};
|
||||
|
||||
EXPECT_TRUE(RD->hasAttr<RandomizeLayoutAttr>());
|
||||
EXPECT_TRUE(RD->isRandomized());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, MismatchedAttrsDeclVsDef) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test __attribute__((randomize_layout));
|
||||
struct test {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
} __attribute__((no_randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
DiagnosticsEngine &Diags = AST->getDiagnostics();
|
||||
|
||||
EXPECT_FALSE(Diags.hasFatalErrorOccurred());
|
||||
EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());
|
||||
EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());
|
||||
EXPECT_EQ(Diags.getNumWarnings(), 1u);
|
||||
EXPECT_EQ(Diags.getNumErrors(), 0u);
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, MismatchedAttrsRandomizeVsNoRandomize) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test2 {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
} __attribute__((randomize_layout)) __attribute__((no_randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
DiagnosticsEngine &Diags = AST->getDiagnostics();
|
||||
|
||||
EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
|
||||
EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
|
||||
EXPECT_EQ(Diags.getNumWarnings(), 0u);
|
||||
EXPECT_EQ(Diags.getNumErrors(), 1u);
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, MismatchedAttrsNoRandomizeVsRandomize) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test3 {
|
||||
int bacon;
|
||||
long lettuce;
|
||||
long long tomato;
|
||||
float mayonnaise;
|
||||
} __attribute__((no_randomize_layout)) __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_TRUE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
DiagnosticsEngine &Diags = AST->getDiagnostics();
|
||||
|
||||
EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());
|
||||
EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());
|
||||
EXPECT_EQ(Diags.getNumWarnings(), 0u);
|
||||
EXPECT_EQ(Diags.getNumErrors(), 1u);
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, CheckAdjacentBitfieldsRemainAdjacentAfterRandomization) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test {
|
||||
int a;
|
||||
int b;
|
||||
int x : 1;
|
||||
int y : 1;
|
||||
int z : 1;
|
||||
int c;
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
||||
|
||||
const field_names Expected = {"a", "b", "c", "x", "y", "z"};
|
||||
const field_names Subseq = {"x", "y", "z"};
|
||||
const field_names Actual = getFieldNamesFromRecord(RD);
|
||||
|
||||
EXPECT_TRUE(isSubsequence(Actual, Subseq));
|
||||
EXPECT_EQ(Expected, Actual);
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, CheckVariableLengthArrayMemberRemainsAtEndOfStructure) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test {
|
||||
int a;
|
||||
double b;
|
||||
short c;
|
||||
char name[];
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD = getRecordDeclFromAST(AST->getASTContext(), "test");
|
||||
const field_names Expected = {"c", "a", "name", "b"};
|
||||
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, RandstructDoesNotOverrideThePackedAttr) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test_struct {
|
||||
char a;
|
||||
float b[3];
|
||||
short c;
|
||||
int d;
|
||||
} __attribute__((packed, randomize_layout));
|
||||
|
||||
struct another_struct {
|
||||
char a;
|
||||
char b[5];
|
||||
int c;
|
||||
} __attribute__((packed, randomize_layout));
|
||||
|
||||
struct last_struct {
|
||||
char a;
|
||||
long long b;
|
||||
int c[];
|
||||
} __attribute__((packed, randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
// FIXME (?): calling getASTRecordLayout is probably a necessary evil so that
|
||||
// Clang's RecordBuilders can actually flesh out the information like
|
||||
// alignment, etc.
|
||||
{
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "test_struct");
|
||||
const ASTRecordLayout *Layout =
|
||||
&AST->getASTContext().getASTRecordLayout(RD);
|
||||
const field_names Expected = {"b", "a", "c", "d"};
|
||||
|
||||
EXPECT_EQ(19, Layout->getSize().getQuantity());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
{
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "another_struct");
|
||||
const ASTRecordLayout *Layout =
|
||||
&AST->getASTContext().getASTRecordLayout(RD);
|
||||
const field_names Expected = {"c", "b", "a"};
|
||||
|
||||
EXPECT_EQ(10, Layout->getSize().getQuantity());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
{
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "last_struct");
|
||||
const ASTRecordLayout *Layout =
|
||||
&AST->getASTContext().getASTRecordLayout(RD);
|
||||
const field_names Expected = {"a", "c", "b"};
|
||||
|
||||
EXPECT_EQ(9, Layout->getSize().getQuantity());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, ZeroWidthBitfieldsSeparateAllocationUnits) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test_struct {
|
||||
int a : 1;
|
||||
int : 0;
|
||||
int b : 1;
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "test_struct");
|
||||
const field_names Expected = {"b", "a", ""};
|
||||
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, RandstructDoesNotRandomizeUnionFieldOrder) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
union test_union {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "test_union");
|
||||
const field_names Expected = {"a", "b", "c", "d", "e", "f"};
|
||||
|
||||
EXPECT_FALSE(RD->isRandomized());
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
|
||||
TEST(RANDSTRUCT_TEST, AnonymousStructsAndUnionsRetainFieldOrder) {
|
||||
const std::unique_ptr<ASTUnit> AST = makeAST(R"c(
|
||||
struct test_struct {
|
||||
int a;
|
||||
struct sub_struct {
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
} __attribute__((randomize_layout)) s;
|
||||
int f;
|
||||
struct {
|
||||
int g;
|
||||
int h;
|
||||
int i;
|
||||
int j;
|
||||
int k;
|
||||
};
|
||||
int l;
|
||||
union {
|
||||
int m;
|
||||
int n;
|
||||
int o;
|
||||
int p;
|
||||
int q;
|
||||
};
|
||||
int r;
|
||||
} __attribute__((randomize_layout));
|
||||
)c");
|
||||
|
||||
EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
|
||||
|
||||
const RecordDecl *RD =
|
||||
getRecordDeclFromAST(AST->getASTContext(), "test_struct");
|
||||
const field_names Expected = {"", "l", "r", "f", "a", "s", ""};
|
||||
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
|
||||
bool AnonStructTested = false;
|
||||
bool AnonUnionTested = false;
|
||||
for (const Decl *D : RD->decls())
|
||||
if (const FieldDecl *FD = dyn_cast<FieldDecl>(D)) {
|
||||
if (const auto *Record = FD->getType()->getAs<RecordType>()) {
|
||||
RD = Record->getDecl();
|
||||
if (RD->isAnonymousStructOrUnion()) {
|
||||
if (RD->isUnion()) {
|
||||
const field_names Expected = {"m", "n", "o", "p", "q"};
|
||||
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
AnonUnionTested = true;
|
||||
} else {
|
||||
const field_names Expected = {"g", "h", "i", "j", "k"};
|
||||
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
AnonStructTested = true;
|
||||
}
|
||||
} else if (RD->isStruct()) {
|
||||
const field_names Expected = {"c", "b", "e", "d", "f"};
|
||||
EXPECT_EQ(Expected, getFieldNamesFromRecord(RD));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(AnonStructTested);
|
||||
EXPECT_TRUE(AnonUnionTested);
|
||||
}
|
||||
|
||||
} // namespace ast_matchers
|
||||
} // namespace clang
|
|
@ -126,7 +126,6 @@ static_library("AST") {
|
|||
"ParentMapContext.cpp",
|
||||
"PrintfFormatString.cpp",
|
||||
"QualTypeNames.cpp",
|
||||
"Randstruct.cpp",
|
||||
"RawCommentList.cpp",
|
||||
"RecordLayout.cpp",
|
||||
"RecordLayoutBuilder.cpp",
|
||||
|
|
|
@ -34,7 +34,6 @@ unittest("ASTTests") {
|
|||
"EvaluateAsRValueTest.cpp",
|
||||
"ExternalASTSourceTest.cpp",
|
||||
"NamedDeclPrinterTest.cpp",
|
||||
"RandstructTest.cpp",
|
||||
"RecursiveASTVisitorTest.cpp",
|
||||
"SizelessTypesTest.cpp",
|
||||
"SourceLocationTest.cpp",
|
||||
|
|
Loading…
Reference in New Issue