Index: lib/AST/RecordLayoutBuilder.cpp =================================================================== --- lib/AST/RecordLayoutBuilder.cpp +++ lib/AST/RecordLayoutBuilder.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/SpecialCaseList.h" using namespace clang; @@ -651,8 +652,10 @@ void Layout(const CXXRecordDecl *D); void Layout(const ObjCInterfaceDecl *D); + bool MayInsertExtraPaddingAfterField(const RecordDecl *RD, + const FieldDecl *FD); void LayoutFields(const RecordDecl *D); - void LayoutField(const FieldDecl *D); + void LayoutField(const FieldDecl *D, bool InsertPoisonedRedzone); void LayoutWideBitField(uint64_t FieldSize, uint64_t TypeSize, bool FieldPacked, const FieldDecl *D); void LayoutBitField(const FieldDecl *D); @@ -1331,18 +1334,68 @@ // Layout each ivar sequentially. for (const ObjCIvarDecl *IVD = D->all_declared_ivar_begin(); IVD; IVD = IVD->getNextIvar()) - LayoutField(IVD); + LayoutField(IVD, false); // Finally, round the size of the total struct up to the alignment of the // struct itself. FinishLayout(D); } +// Returns true if we want to insert an extra padding in RD after FD. +// These padding are added to help AddressSanitizer detect intra-object-overflow +// bugs. Later in CogeGen an extra code will be emitted to poison these +// extra paddings. +bool RecordLayoutBuilder::MayInsertExtraPaddingAfterField(const RecordDecl *RD, + const FieldDecl *FD) { + if (!Context.getLangOpts().Sanitize.Address || + !Context.getLangOpts().Sanitize.AddressFieldPadding) + return false; + const CXXRecordDecl *CXXRD = dyn_cast(RD); + // We may be able to relax some of these requirements. + const char *ReasonToReject = 0; + if (!CXXRD) + ReasonToReject = "not C++"; + else if (CXXRD->hasAttr()) + ReasonToReject = "packed"; + else if (CXXRD->isUnion()) + ReasonToReject = "union"; + else if (CXXRD->isTriviallyCopyable()) + ReasonToReject = "trivially copyable"; + else if (CXXRD->hasSimpleDestructor()) + ReasonToReject = "has simple DTOR"; + else if (CXXRD->isStandardLayout()) + ReasonToReject = "standard layout"; + + // FIXME (before commit): where should this SpecialCaseList reside? + // In RecordLayoutBuilder? In global scope? + // Also, can we reuse the existing -fsanitize-blacklist flag to pass + // the file path (i.e. the same blacklist file will be used in AST and + // CogeGen), or we should have another flag? + auto SCL = llvm::SpecialCaseList::createOrDie( + "/tmp/blacklist.txt"); + StringRef Filename = + Context.getSourceManager().getFilename(RD->getLocation()); + if (SCL->inSection("src", Filename)) + ReasonToReject = "blacklisted by src"; + + // FIXME (before commit): what flag to use for debug output? + if (0) { + const char *ToPrint = ReasonToReject ? ReasonToReject : "ACCEPTED"; + llvm::errs() << "ASAN_FIELD_PADDING: " << Filename << " " << *RD + << "::" << *FD << " : " << ToPrint << "\n"; + } + return ReasonToReject == 0; +} + void RecordLayoutBuilder::LayoutFields(const RecordDecl *D) { // Layout each field, for now, just sequentially, respecting alignment. In // the future, this will need to be tweakable by targets. - for (const auto *Field : D->fields()) - LayoutField(Field); + SmallVector Fields(D->field_begin(), D->field_end()); + // FIXME (before commit): is there a way to know that the field is last + // w/o this temporary vector? + for (size_t i = 0, e = Fields.size(); i < e; i++) + LayoutField(Fields[i], + i + 1 != e && MayInsertExtraPaddingAfterField(D, Fields[i])); } void RecordLayoutBuilder::LayoutWideBitField(uint64_t FieldSize, @@ -1645,7 +1698,8 @@ Context.toCharUnitsFromBits(UnpackedFieldAlign)); } -void RecordLayoutBuilder::LayoutField(const FieldDecl *D) { +void RecordLayoutBuilder::LayoutField(const FieldDecl *D, + bool InsertPoisonedRedzone) { if (D->isBitField()) { LayoutBitField(D); return; @@ -1749,6 +1803,15 @@ Context.toBits(UnpackedFieldOffset), Context.toBits(UnpackedFieldAlign), FieldPacked, D); + if (InsertPoisonedRedzone) { + CharUnits ASanAlignment = CharUnits::fromQuantity(8); + CharUnits ExtraSizeForAsan = ASanAlignment; + if (FieldSize % ASanAlignment) + ExtraSizeForAsan += + ASanAlignment - CharUnits::fromQuantity(FieldSize % ASanAlignment); + FieldSize += ExtraSizeForAsan; + } + // Reserve space for this field. uint64_t FieldSizeInBits = Context.toBits(FieldSize); if (IsUnion) Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -692,8 +692,74 @@ return true; } +// Emit code in CTOR (Prologue==true) or DTOR(Prologue==false) +// to poison the extra field paddings inserted under +// -fsanitize-address-field-padding=1|2. +void CodeGenFunction::EmitAsanPrologueOrEpilogue(bool Prologue) { + if (!getLangOpts().Sanitize.AddressFieldPadding) return; + const CXXRecordDecl *ClassDecl = + Prologue ? cast(CurGD.getDecl())->getParent() + : cast(CurGD.getDecl())->getParent(); + if (ClassDecl->hasAttr() || ClassDecl->isStandardLayout() || + ClassDecl->isTriviallyCopyable()) + return; + + struct SizeAndOffset { + uint64_t size; + uint64_t offset; + }; + + unsigned PtrSize = CGM.getDataLayout().getPointerSizeInBits(); + const ASTRecordLayout &Info = getContext().getASTRecordLayout(ClassDecl); + + // Populate sizes and offsets of fields. + // FIXME: can we get those from a single source? + SmallVector SSV(Info.getFieldCount()); + for (unsigned i = 0, e = Info.getFieldCount(); i != e; ++i) + SSV[i].offset = Info.getFieldOffset(i) / 8; + + + size_t n_fields = 0; + for (const auto *Field : ClassDecl->fields()) { + const FieldDecl *D = Field; + std::pair FieldInfo = + getContext().getTypeInfoInChars(D->getType()); + CharUnits FieldSize = FieldInfo.first; + assert(n_fields < SSV.size()); + SSV[n_fields].size = D->isBitField() ? 0 : FieldSize.getQuantity(); + n_fields++; + } + assert(n_fields == SSV.size()); + if (SSV.size() <= 1) return; + + // We will insert calls to __asan_* run-time functions. + // LLVM AddressSanitizer pass may decide to inline them later. + llvm::Type *PtrIntTy = Builder.getIntNTy(PtrSize); + llvm::Type *Args[2] = {PtrIntTy, PtrIntTy}; + llvm::FunctionType *FTy = + llvm::FunctionType::get(CGM.VoidTy, Args, false); + llvm::Constant *F = CGM.CreateRuntimeFunction( + FTy, Prologue ? "__asan_poison_intra_object_redzone" + : "__asan_unpoison_intra_object_redzone"); + + llvm::Value *ThisPtr = LoadCXXThis(); + ThisPtr = Builder.CreatePtrToInt(ThisPtr, PtrIntTy); + + // For each field (but the last) check if it has sufficient padding, + // if so (un)poison it with a call. + for (size_t i = 0; i < SSV.size() - 1; i++) { + uint64_t PoisonSize = SSV[i+1].offset - SSV[i].offset - SSV[i].size; + uint64_t EndOffset = SSV[i].offset + SSV[i].size; + if (PoisonSize < 8 || !SSV[i].size || (SSV[i+1].offset % 8) != 0) continue; + Builder.CreateCall2( + F, Builder.CreateAdd(ThisPtr, Builder.getIntN(PtrSize, EndOffset)), + Builder.getIntN(PtrSize, PoisonSize)); + } +} + /// EmitConstructorBody - Emits the body of the current constructor. void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) { + EmitAsanPrologueOrEpilogue(true); const CXXConstructorDecl *Ctor = cast(CurGD.getDecl()); CXXCtorType CtorType = CurGD.getCtorType(); @@ -782,7 +848,10 @@ FirstField(nullptr), LastField(nullptr), FirstFieldOffset(0), LastFieldOffset(0), LastAddedFieldIndex(0) {} - static bool isMemcpyableField(FieldDecl *F) { + bool isMemcpyableField(FieldDecl *F) const { + // Never memcpy fields when we are adding poisoned paddings. + if (CGF.getContext().getLangOpts().Sanitize.AddressFieldPadding) + return false; Qualifiers Qual = F->getType().getQualifiers(); if (Qual.hasVolatile() || Qual.hasObjCLifetime()) return false; @@ -1282,6 +1351,7 @@ bool isTryBody = (Body && isa(Body)); if (isTryBody) EnterCXXTryStmt(*cast(Body), true); + EmitAsanPrologueOrEpilogue(false); // Enter the epilogue cleanups. RunCleanupsScope DtorEpilogue(*this); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1268,6 +1268,7 @@ void EmitLambdaBlockInvokeBody(); void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD); void EmitLambdaStaticInvokeFunction(const CXXMethodDecl *MD); + void EmitAsanPrologueOrEpilogue(bool Prologue); /// EmitReturnBlock - Emit the unified return block, trying to avoid its /// emission when possible.