Home | History | Annotate | Line # | Download | only in Checkers
      1      1.1  joerg //=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
      2      1.1  joerg //
      3      1.1  joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4      1.1  joerg // See https://llvm.org/LICENSE.txt for license information.
      5      1.1  joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6      1.1  joerg //
      7      1.1  joerg //===----------------------------------------------------------------------===//
      8      1.1  joerg //
      9      1.1  joerg //  This file defines a checker that checks for padding that could be
     10      1.1  joerg //  removed by re-ordering members.
     11      1.1  joerg //
     12      1.1  joerg //===----------------------------------------------------------------------===//
     13      1.1  joerg 
     14      1.1  joerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     15      1.1  joerg #include "clang/AST/CharUnits.h"
     16      1.1  joerg #include "clang/AST/DeclTemplate.h"
     17      1.1  joerg #include "clang/AST/RecordLayout.h"
     18      1.1  joerg #include "clang/AST/RecursiveASTVisitor.h"
     19      1.1  joerg #include "clang/Driver/DriverDiagnostic.h"
     20      1.1  joerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     21      1.1  joerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     22      1.1  joerg #include "clang/StaticAnalyzer/Core/Checker.h"
     23      1.1  joerg #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
     24      1.1  joerg #include "llvm/ADT/SmallString.h"
     25      1.1  joerg #include "llvm/Support/MathExtras.h"
     26      1.1  joerg #include "llvm/Support/raw_ostream.h"
     27      1.1  joerg #include <numeric>
     28      1.1  joerg 
     29      1.1  joerg using namespace clang;
     30      1.1  joerg using namespace ento;
     31      1.1  joerg 
     32      1.1  joerg namespace {
     33      1.1  joerg class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
     34      1.1  joerg private:
     35      1.1  joerg   mutable std::unique_ptr<BugType> PaddingBug;
     36      1.1  joerg   mutable BugReporter *BR;
     37      1.1  joerg 
     38      1.1  joerg public:
     39      1.1  joerg   int64_t AllowedPad;
     40      1.1  joerg 
     41      1.1  joerg   void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
     42      1.1  joerg                     BugReporter &BRArg) const {
     43      1.1  joerg     BR = &BRArg;
     44      1.1  joerg 
     45      1.1  joerg     // The calls to checkAST* from AnalysisConsumer don't
     46      1.1  joerg     // visit template instantiations or lambda classes. We
     47      1.1  joerg     // want to visit those, so we make our own RecursiveASTVisitor.
     48      1.1  joerg     struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
     49      1.1  joerg       const PaddingChecker *Checker;
     50      1.1  joerg       bool shouldVisitTemplateInstantiations() const { return true; }
     51      1.1  joerg       bool shouldVisitImplicitCode() const { return true; }
     52      1.1  joerg       explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}
     53      1.1  joerg       bool VisitRecordDecl(const RecordDecl *RD) {
     54      1.1  joerg         Checker->visitRecord(RD);
     55      1.1  joerg         return true;
     56      1.1  joerg       }
     57      1.1  joerg       bool VisitVarDecl(const VarDecl *VD) {
     58      1.1  joerg         Checker->visitVariable(VD);
     59      1.1  joerg         return true;
     60      1.1  joerg       }
     61      1.1  joerg       // TODO: Visit array new and mallocs for arrays.
     62      1.1  joerg     };
     63      1.1  joerg 
     64      1.1  joerg     LocalVisitor visitor(this);
     65      1.1  joerg     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
     66      1.1  joerg   }
     67      1.1  joerg 
     68      1.1  joerg   /// Look for records of overly padded types. If padding *
     69      1.1  joerg   /// PadMultiplier exceeds AllowedPad, then generate a report.
     70      1.1  joerg   /// PadMultiplier is used to share code with the array padding
     71      1.1  joerg   /// checker.
     72      1.1  joerg   void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {
     73      1.1  joerg     if (shouldSkipDecl(RD))
     74      1.1  joerg       return;
     75      1.1  joerg 
     76      1.1  joerg     // TODO: Figure out why we are going through declarations and not only
     77      1.1  joerg     // definitions.
     78      1.1  joerg     if (!(RD = RD->getDefinition()))
     79      1.1  joerg       return;
     80      1.1  joerg 
     81      1.1  joerg     // This is the simplest correct case: a class with no fields and one base
     82      1.1  joerg     // class. Other cases are more complicated because of how the base classes
     83      1.1  joerg     // & fields might interact, so we don't bother dealing with them.
     84      1.1  joerg     // TODO: Support other combinations of base classes and fields.
     85      1.1  joerg     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
     86      1.1  joerg       if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
     87      1.1  joerg         return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
     88      1.1  joerg                            PadMultiplier);
     89      1.1  joerg 
     90      1.1  joerg     auto &ASTContext = RD->getASTContext();
     91      1.1  joerg     const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
     92      1.1  joerg     assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
     93      1.1  joerg 
     94      1.1  joerg     CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
     95      1.1  joerg     if (BaselinePad.isZero())
     96      1.1  joerg       return;
     97      1.1  joerg 
     98      1.1  joerg     CharUnits OptimalPad;
     99      1.1  joerg     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
    100      1.1  joerg     std::tie(OptimalPad, OptimalFieldsOrder) =
    101      1.1  joerg         calculateOptimalPad(RD, ASTContext, RL);
    102      1.1  joerg 
    103      1.1  joerg     CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
    104      1.1  joerg     if (DiffPad.getQuantity() <= AllowedPad) {
    105      1.1  joerg       assert(!DiffPad.isNegative() && "DiffPad should not be negative");
    106      1.1  joerg       // There is not enough excess padding to trigger a warning.
    107      1.1  joerg       return;
    108      1.1  joerg     }
    109      1.1  joerg     reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
    110      1.1  joerg   }
    111      1.1  joerg 
    112      1.1  joerg   /// Look for arrays of overly padded types. If the padding of the
    113      1.1  joerg   /// array type exceeds AllowedPad, then generate a report.
    114      1.1  joerg   void visitVariable(const VarDecl *VD) const {
    115      1.1  joerg     const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
    116      1.1  joerg     if (ArrTy == nullptr)
    117      1.1  joerg       return;
    118      1.1  joerg     uint64_t Elts = 0;
    119      1.1  joerg     if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
    120      1.1  joerg       Elts = CArrTy->getSize().getZExtValue();
    121      1.1  joerg     if (Elts == 0)
    122      1.1  joerg       return;
    123      1.1  joerg     const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();
    124      1.1  joerg     if (RT == nullptr)
    125      1.1  joerg       return;
    126      1.1  joerg 
    127      1.1  joerg     // TODO: Recurse into the fields to see if they have excess padding.
    128      1.1  joerg     visitRecord(RT->getDecl(), Elts);
    129      1.1  joerg   }
    130      1.1  joerg 
    131      1.1  joerg   bool shouldSkipDecl(const RecordDecl *RD) const {
    132      1.1  joerg     // TODO: Figure out why we are going through declarations and not only
    133      1.1  joerg     // definitions.
    134      1.1  joerg     if (!(RD = RD->getDefinition()))
    135      1.1  joerg       return true;
    136      1.1  joerg     auto Location = RD->getLocation();
    137      1.1  joerg     // If the construct doesn't have a source file, then it's not something
    138      1.1  joerg     // we want to diagnose.
    139      1.1  joerg     if (!Location.isValid())
    140      1.1  joerg       return true;
    141      1.1  joerg     SrcMgr::CharacteristicKind Kind =
    142      1.1  joerg         BR->getSourceManager().getFileCharacteristic(Location);
    143      1.1  joerg     // Throw out all records that come from system headers.
    144      1.1  joerg     if (Kind != SrcMgr::C_User)
    145      1.1  joerg       return true;
    146      1.1  joerg 
    147      1.1  joerg     // Not going to attempt to optimize unions.
    148      1.1  joerg     if (RD->isUnion())
    149      1.1  joerg       return true;
    150      1.1  joerg     if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
    151      1.1  joerg       // Tail padding with base classes ends up being very complicated.
    152      1.1  joerg       // We will skip objects with base classes for now, unless they do not
    153      1.1  joerg       // have fields.
    154      1.1  joerg       // TODO: Handle more base class scenarios.
    155      1.1  joerg       if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
    156      1.1  joerg         return true;
    157      1.1  joerg       if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
    158      1.1  joerg         return true;
    159      1.1  joerg       // Virtual bases are complicated, skipping those for now.
    160      1.1  joerg       if (CXXRD->getNumVBases() != 0)
    161      1.1  joerg         return true;
    162      1.1  joerg       // Can't layout a template, so skip it. We do still layout the
    163      1.1  joerg       // instantiations though.
    164      1.1  joerg       if (CXXRD->getTypeForDecl()->isDependentType())
    165      1.1  joerg         return true;
    166      1.1  joerg       if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
    167      1.1  joerg         return true;
    168      1.1  joerg     }
    169      1.1  joerg     // How do you reorder fields if you haven't got any?
    170      1.1  joerg     else if (RD->field_empty())
    171      1.1  joerg       return true;
    172      1.1  joerg 
    173      1.1  joerg     auto IsTrickyField = [](const FieldDecl *FD) -> bool {
    174      1.1  joerg       // Bitfield layout is hard.
    175      1.1  joerg       if (FD->isBitField())
    176      1.1  joerg         return true;
    177      1.1  joerg 
    178      1.1  joerg       // Variable length arrays are tricky too.
    179      1.1  joerg       QualType Ty = FD->getType();
    180      1.1  joerg       if (Ty->isIncompleteArrayType())
    181      1.1  joerg         return true;
    182      1.1  joerg       return false;
    183      1.1  joerg     };
    184      1.1  joerg 
    185      1.1  joerg     if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
    186      1.1  joerg       return true;
    187      1.1  joerg     return false;
    188      1.1  joerg   }
    189      1.1  joerg 
    190      1.1  joerg   static CharUnits calculateBaselinePad(const RecordDecl *RD,
    191      1.1  joerg                                         const ASTContext &ASTContext,
    192      1.1  joerg                                         const ASTRecordLayout &RL) {
    193      1.1  joerg     CharUnits PaddingSum;
    194      1.1  joerg     CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
    195      1.1  joerg     for (const FieldDecl *FD : RD->fields()) {
    196      1.1  joerg       // This checker only cares about the padded size of the
    197      1.1  joerg       // field, and not the data size. If the field is a record
    198      1.1  joerg       // with tail padding, then we won't put that number in our
    199      1.1  joerg       // total because reordering fields won't fix that problem.
    200      1.1  joerg       CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());
    201      1.1  joerg       auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());
    202      1.1  joerg       CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);
    203      1.1  joerg       PaddingSum += (FieldOffset - Offset);
    204      1.1  joerg       Offset = FieldOffset + FieldSize;
    205      1.1  joerg     }
    206      1.1  joerg     PaddingSum += RL.getSize() - Offset;
    207      1.1  joerg     return PaddingSum;
    208      1.1  joerg   }
    209      1.1  joerg 
    210      1.1  joerg   /// Optimal padding overview:
    211      1.1  joerg   /// 1.  Find a close approximation to where we can place our first field.
    212      1.1  joerg   ///     This will usually be at offset 0.
    213      1.1  joerg   /// 2.  Try to find the best field that can legally be placed at the current
    214      1.1  joerg   ///     offset.
    215      1.1  joerg   ///   a.  "Best" is the largest alignment that is legal, but smallest size.
    216      1.1  joerg   ///       This is to account for overly aligned types.
    217      1.1  joerg   /// 3.  If no fields can fit, pad by rounding the current offset up to the
    218      1.1  joerg   ///     smallest alignment requirement of our fields. Measure and track the
    219      1.1  joerg   //      amount of padding added. Go back to 2.
    220      1.1  joerg   /// 4.  Increment the current offset by the size of the chosen field.
    221      1.1  joerg   /// 5.  Remove the chosen field from the set of future possibilities.
    222      1.1  joerg   /// 6.  Go back to 2 if there are still unplaced fields.
    223      1.1  joerg   /// 7.  Add tail padding by rounding the current offset up to the structure
    224      1.1  joerg   ///     alignment. Track the amount of padding added.
    225      1.1  joerg 
    226      1.1  joerg   static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
    227      1.1  joerg   calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,
    228      1.1  joerg                       const ASTRecordLayout &RL) {
    229      1.1  joerg     struct FieldInfo {
    230      1.1  joerg       CharUnits Align;
    231      1.1  joerg       CharUnits Size;
    232      1.1  joerg       const FieldDecl *Field;
    233      1.1  joerg       bool operator<(const FieldInfo &RHS) const {
    234      1.1  joerg         // Order from small alignments to large alignments,
    235      1.1  joerg         // then large sizes to small sizes.
    236      1.1  joerg         // then large field indices to small field indices
    237      1.1  joerg         return std::make_tuple(Align, -Size,
    238      1.1  joerg                                Field ? -static_cast<int>(Field->getFieldIndex())
    239      1.1  joerg                                      : 0) <
    240      1.1  joerg                std::make_tuple(
    241      1.1  joerg                    RHS.Align, -RHS.Size,
    242      1.1  joerg                    RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
    243      1.1  joerg                              : 0);
    244      1.1  joerg       }
    245      1.1  joerg     };
    246      1.1  joerg     SmallVector<FieldInfo, 20> Fields;
    247      1.1  joerg     auto GatherSizesAndAlignments = [](const FieldDecl *FD) {
    248      1.1  joerg       FieldInfo RetVal;
    249      1.1  joerg       RetVal.Field = FD;
    250      1.1  joerg       auto &Ctx = FD->getASTContext();
    251  1.1.1.2  joerg       auto Info = Ctx.getTypeInfoInChars(FD->getType());
    252  1.1.1.2  joerg       RetVal.Size = Info.Width;
    253  1.1.1.2  joerg       RetVal.Align = Info.Align;
    254      1.1  joerg       assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
    255      1.1  joerg       if (auto Max = FD->getMaxAlignment())
    256      1.1  joerg         RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
    257      1.1  joerg       return RetVal;
    258      1.1  joerg     };
    259      1.1  joerg     std::transform(RD->field_begin(), RD->field_end(),
    260      1.1  joerg                    std::back_inserter(Fields), GatherSizesAndAlignments);
    261      1.1  joerg     llvm::sort(Fields);
    262      1.1  joerg     // This lets us skip over vptrs and non-virtual bases,
    263      1.1  joerg     // so that we can just worry about the fields in our object.
    264      1.1  joerg     // Note that this does cause us to miss some cases where we
    265      1.1  joerg     // could pack more bytes in to a base class's tail padding.
    266      1.1  joerg     CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
    267      1.1  joerg     CharUnits NewPad;
    268      1.1  joerg     SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
    269      1.1  joerg     while (!Fields.empty()) {
    270      1.1  joerg       unsigned TrailingZeros =
    271      1.1  joerg           llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());
    272      1.1  joerg       // If NewOffset is zero, then countTrailingZeros will be 64. Shifting
    273      1.1  joerg       // 64 will overflow our unsigned long long. Shifting 63 will turn
    274      1.1  joerg       // our long long (and CharUnits internal type) negative. So shift 62.
    275      1.1  joerg       long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
    276      1.1  joerg       CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);
    277      1.1  joerg       FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};
    278      1.1  joerg 
    279      1.1  joerg       // In the typical case, this will find the last element
    280      1.1  joerg       // of the vector. We won't find a middle element unless
    281      1.1  joerg       // we started on a poorly aligned address or have an overly
    282      1.1  joerg       // aligned field.
    283      1.1  joerg       auto Iter = llvm::upper_bound(Fields, InsertPoint);
    284      1.1  joerg       if (Iter != Fields.begin()) {
    285      1.1  joerg         // We found a field that we can layout with the current alignment.
    286      1.1  joerg         --Iter;
    287      1.1  joerg         NewOffset += Iter->Size;
    288      1.1  joerg         OptimalFieldsOrder.push_back(Iter->Field);
    289      1.1  joerg         Fields.erase(Iter);
    290      1.1  joerg       } else {
    291      1.1  joerg         // We are poorly aligned, and we need to pad in order to layout another
    292      1.1  joerg         // field. Round up to at least the smallest field alignment that we
    293      1.1  joerg         // currently have.
    294      1.1  joerg         CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);
    295      1.1  joerg         NewPad += NextOffset - NewOffset;
    296      1.1  joerg         NewOffset = NextOffset;
    297      1.1  joerg       }
    298      1.1  joerg     }
    299      1.1  joerg     // Calculate tail padding.
    300      1.1  joerg     CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());
    301      1.1  joerg     NewPad += NewSize - NewOffset;
    302      1.1  joerg     return {NewPad, std::move(OptimalFieldsOrder)};
    303      1.1  joerg   }
    304      1.1  joerg 
    305      1.1  joerg   void reportRecord(
    306      1.1  joerg       const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
    307      1.1  joerg       const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
    308      1.1  joerg     if (!PaddingBug)
    309      1.1  joerg       PaddingBug =
    310      1.1  joerg           std::make_unique<BugType>(this, "Excessive Padding", "Performance");
    311      1.1  joerg 
    312      1.1  joerg     SmallString<100> Buf;
    313      1.1  joerg     llvm::raw_svector_ostream Os(Buf);
    314      1.1  joerg     Os << "Excessive padding in '";
    315      1.1  joerg     Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),
    316      1.1  joerg                                 LangOptions())
    317      1.1  joerg        << "'";
    318      1.1  joerg 
    319      1.1  joerg     if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
    320      1.1  joerg       // TODO: make this show up better in the console output and in
    321      1.1  joerg       // the HTML. Maybe just make it show up in HTML like the path
    322      1.1  joerg       // diagnostics show.
    323      1.1  joerg       SourceLocation ILoc = TSD->getPointOfInstantiation();
    324      1.1  joerg       if (ILoc.isValid())
    325      1.1  joerg         Os << " instantiated here: "
    326      1.1  joerg            << ILoc.printToString(BR->getSourceManager());
    327      1.1  joerg     }
    328      1.1  joerg 
    329      1.1  joerg     Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
    330      1.1  joerg        << OptimalPad.getQuantity() << " is optimal). \n"
    331      1.1  joerg        << "Optimal fields order: \n";
    332      1.1  joerg     for (const auto *FD : OptimalFieldsOrder)
    333      1.1  joerg       Os << FD->getName() << ", \n";
    334      1.1  joerg     Os << "consider reordering the fields or adding explicit padding "
    335      1.1  joerg           "members.";
    336      1.1  joerg 
    337      1.1  joerg     PathDiagnosticLocation CELoc =
    338      1.1  joerg         PathDiagnosticLocation::create(RD, BR->getSourceManager());
    339      1.1  joerg     auto Report =
    340      1.1  joerg         std::make_unique<BasicBugReport>(*PaddingBug, Os.str(), CELoc);
    341      1.1  joerg     Report->setDeclWithIssue(RD);
    342      1.1  joerg     Report->addRange(RD->getSourceRange());
    343      1.1  joerg     BR->emitReport(std::move(Report));
    344      1.1  joerg   }
    345      1.1  joerg };
    346      1.1  joerg } // namespace
    347      1.1  joerg 
    348      1.1  joerg void ento::registerPaddingChecker(CheckerManager &Mgr) {
    349      1.1  joerg   auto *Checker = Mgr.registerChecker<PaddingChecker>();
    350      1.1  joerg   Checker->AllowedPad = Mgr.getAnalyzerOptions()
    351      1.1  joerg           .getCheckerIntegerOption(Checker, "AllowedPad");
    352      1.1  joerg   if (Checker->AllowedPad < 0)
    353      1.1  joerg     Mgr.reportInvalidCheckerOptionValue(
    354      1.1  joerg         Checker, "AllowedPad", "a non-negative value");
    355      1.1  joerg }
    356      1.1  joerg 
    357  1.1.1.2  joerg bool ento::shouldRegisterPaddingChecker(const CheckerManager &mgr) {
    358      1.1  joerg   return true;
    359      1.1  joerg }
    360