diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp | 93 |
1 files changed, 77 insertions, 16 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 5e6e10541483..3ba063ddc821 100644 --- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -23,12 +23,71 @@ using namespace ento; namespace { class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { - mutable OwningPtr<BuiltinBug> BT; + mutable std::unique_ptr<BuiltinBug> BT; + public: void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; }; } +/// Check if we are casting to a struct with a flexible array at the end. +/// \code +/// struct foo { +/// size_t len; +/// struct bar data[]; +/// }; +/// \endcode +/// or +/// \code +/// struct foo { +/// size_t len; +/// struct bar data[0]; +/// } +/// \endcode +/// In these cases it is also valid to allocate size of struct foo + a multiple +/// of struct bar. +static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize, + CharUnits TypeSize, QualType ToPointeeTy) { + const RecordType *RT = ToPointeeTy->getAs<RecordType>(); + if (!RT) + return false; + + const RecordDecl *RD = RT->getDecl(); + RecordDecl::field_iterator Iter(RD->field_begin()); + RecordDecl::field_iterator End(RD->field_end()); + const FieldDecl *Last = nullptr; + for (; Iter != End; ++Iter) + Last = *Iter; + assert(Last && "empty structs should already be handled"); + + const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual(); + CharUnits FlexSize; + if (const ConstantArrayType *ArrayTy = + Ctx.getAsConstantArrayType(Last->getType())) { + FlexSize = Ctx.getTypeSizeInChars(ElemType); + if (ArrayTy->getSize() == 1 && TypeSize > FlexSize) + TypeSize -= FlexSize; + else if (ArrayTy->getSize() != 0) + return false; + } else if (RD->hasFlexibleArrayMember()) { + FlexSize = Ctx.getTypeSizeInChars(ElemType); + } else { + return false; + } + + if (FlexSize.isZero()) + return false; + + CharUnits Left = RegionSize - TypeSize; + if (Left.isNegative()) + return false; + + if (Left % FlexSize == 0) + return true; + + return false; +} + void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { const Expr *E = CE->getSubExpr(); ASTContext &Ctx = C.getASTContext(); @@ -46,11 +105,11 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { ProgramStateRef state = C.getState(); const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion(); - if (R == 0) + if (!R) return; const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); - if (SR == 0) + if (!SR) return; SValBuilder &svalBuilder = C.getSValBuilder(); @@ -66,21 +125,23 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { if (typeSize.isZero()) return; - if (regionSize % typeSize != 0) { - if (ExplodedNode *errorNode = C.generateSink()) { - if (!BT) - BT.reset(new BuiltinBug("Cast region with wrong size.", - "Cast a region whose size is not a multiple of the" - " destination type size.")); - BugReport *R = new BugReport(*BT, BT->getDescription(), - errorNode); - R->addRange(CE->getSourceRange()); - C.emitReport(R); - } + if (regionSize % typeSize == 0) + return; + + if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) + return; + + if (ExplodedNode *errorNode = C.generateSink()) { + if (!BT) + BT.reset(new BuiltinBug(this, "Cast region with wrong size.", + "Cast a region whose size is not a multiple" + " of the destination type size.")); + BugReport *R = new BugReport(*BT, BT->getDescription(), errorNode); + R->addRange(CE->getSourceRange()); + C.emitReport(R); } } - void ento::registerCastSizeChecker(CheckerManager &mgr) { - mgr.registerChecker<CastSizeChecker>(); + mgr.registerChecker<CastSizeChecker>(); } |