diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 230 |
1 files changed, 136 insertions, 94 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index e7edc20ac3e..296997a5941 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -155,31 +155,23 @@ class MallocChecker : public Checker<check::DeadSymbols, check::Location, eval::Assume> { - mutable OwningPtr<BugType> BT_DoubleFree; - mutable OwningPtr<BugType> BT_DoubleDelete; - mutable OwningPtr<BugType> BT_Leak; - mutable OwningPtr<BugType> BT_UseFree; - mutable OwningPtr<BugType> BT_BadFree; - mutable OwningPtr<BugType> BT_MismatchedDealloc; - mutable OwningPtr<BugType> BT_OffsetFree; - mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, - *II_valloc, *II_reallocf, *II_strndup, *II_strdup; - public: MallocChecker() : II_malloc(0), II_free(0), II_realloc(0), II_calloc(0), II_valloc(0), II_reallocf(0), II_strndup(0), II_strdup(0) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. - struct ChecksFilter { - DefaultBool CMallocPessimistic; - DefaultBool CMallocOptimistic; - DefaultBool CNewDeleteChecker; - DefaultBool CNewDeleteLeaksChecker; - DefaultBool CMismatchedDeallocatorChecker; + enum CheckKind { + CK_MallocPessimistic, + CK_MallocOptimistic, + CK_NewDeleteChecker, + CK_NewDeleteLeaksChecker, + CK_MismatchedDeallocatorChecker, + CK_NumCheckKinds }; - ChecksFilter Filter; + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; @@ -207,6 +199,16 @@ public: const char *NL, const char *Sep) const; private: + mutable OwningPtr<BugType> BT_DoubleFree[CK_NumCheckKinds]; + mutable OwningPtr<BugType> BT_DoubleDelete; + mutable OwningPtr<BugType> BT_Leak[CK_NumCheckKinds]; + mutable OwningPtr<BugType> BT_UseFree[CK_NumCheckKinds]; + mutable OwningPtr<BugType> BT_BadFree[CK_NumCheckKinds]; + mutable OwningPtr<BugType> BT_MismatchedDealloc; + mutable OwningPtr<BugType> BT_OffsetFree[CK_NumCheckKinds]; + mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc, + *II_valloc, *II_reallocf, *II_strndup, *II_strdup; + void initIdentifierInfo(ASTContext &C) const; /// \brief Determine family of a deallocation expression. @@ -304,10 +306,12 @@ private: ///@{ /// Tells if a given family/call/symbol is tracked by the current checker. - bool isTrackedByCurrentChecker(AllocationFamily Family) const; - bool isTrackedByCurrentChecker(CheckerContext &C, - const Stmt *AllocDeallocStmt) const; - bool isTrackedByCurrentChecker(CheckerContext &C, SymbolRef Sym) const; + /// Sets CheckKind to the kind of the checker responsible for this + /// family/call/symbol. + Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const; + Optional<CheckKind> getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt) const; + Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); @@ -507,7 +511,7 @@ bool MallocChecker::isAllocationFunction(const FunctionDecl *FD, return true; } - if (Filter.CMallocOptimistic && FD->hasAttrs()) + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) for (specific_attr_iterator<OwnershipAttr> i = FD->specific_attr_begin<OwnershipAttr>(), e = FD->specific_attr_end<OwnershipAttr>(); @@ -529,7 +533,7 @@ bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const return true; } - if (Filter.CMallocOptimistic && FD->hasAttrs()) + if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) for (specific_attr_iterator<OwnershipAttr> i = FD->specific_attr_begin<OwnershipAttr>(), e = FD->specific_attr_end<OwnershipAttr>(); @@ -624,7 +628,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { } } - if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) { + if (ChecksEnabled[CK_MallocOptimistic] || + ChecksEnabled[CK_MismatchedDeallocatorChecker]) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -671,7 +676,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const { - if (!Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_NewDeleteChecker]) if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) checkUseAfterFree(Sym, C, DE->getArgument()); @@ -1092,18 +1097,23 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, RefState::getReleased(Family, ParentExpr)); } -bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const { +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(AllocationFamily Family) const { switch (Family) { case AF_Malloc: { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic) - return false; - return true; + if (ChecksEnabled[CK_MallocOptimistic]) { + return CK_MallocOptimistic; + } else if (ChecksEnabled[CK_MallocPessimistic]) { + return CK_MallocPessimistic; + } + return Optional<MallocChecker::CheckKind>(); } case AF_CXXNew: case AF_CXXNewArray: { - if (!Filter.CNewDeleteChecker) - return false; - return true; + if (ChecksEnabled[CK_NewDeleteChecker]) { + return CK_NewDeleteChecker; + } + return Optional<MallocChecker::CheckKind>(); } case AF_None: { llvm_unreachable("no family"); @@ -1112,18 +1122,18 @@ bool MallocChecker::isTrackedByCurrentChecker(AllocationFamily Family) const { llvm_unreachable("unhandled family"); } -bool -MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, - const Stmt *AllocDeallocStmt) const { - return isTrackedByCurrentChecker(getAllocationFamily(C, AllocDeallocStmt)); +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt) const { + return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt)); } -bool MallocChecker::isTrackedByCurrentChecker(CheckerContext &C, - SymbolRef Sym) const { +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const { const RefState *RS = C.getState()->get<RegionState>(Sym); assert(RS); - return isTrackedByCurrentChecker(RS->getAllocationFamily()); + return getCheckIfTracked(RS->getAllocationFamily()); } bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { @@ -1217,17 +1227,21 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, DeallocExpr)) + Optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(C, DeallocExpr); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_BadFree) - BT_BadFree.reset(new BugType("Bad free", "Memory Error")); - + if (!BT_BadFree[*CheckKind]) + BT_BadFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error")); + SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1253,7 +1267,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, printExpectedAllocName(os, C, DeallocExpr); } - BugReport *R = new BugReport(*BT_BadFree, os.str(), N); + BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N); R->markInteresting(MR); R->addRange(Range); C.emitReport(R); @@ -1267,14 +1281,15 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, SymbolRef Sym, bool OwnershipTransferred) const { - if (!Filter.CMismatchedDeallocatorChecker) + if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) return; if (ExplodedNode *N = C.generateSink()) { if (!BT_MismatchedDealloc) - BT_MismatchedDealloc.reset(new BugType("Bad deallocator", - "Memory Error")); - + BT_MismatchedDealloc.reset( + new BugType(CheckNames[CK_MismatchedDeallocatorChecker], + "Bad deallocator", "Memory Error")); + SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1318,19 +1333,23 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, AllocExpr)) + Optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(C, AllocExpr); + if (!CheckKind.hasValue()) return; ExplodedNode *N = C.generateSink(); if (N == NULL) return; - if (!BT_OffsetFree) - BT_OffsetFree.reset(new BugType("Offset free", "Memory Error")); + if (!BT_OffsetFree[*CheckKind]) + BT_OffsetFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Offset free", "Memory Error")); SmallString<100> buf; llvm::raw_svector_ostream os(buf); @@ -1361,7 +1380,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, else os << "allocated memory"; - BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N); + BugReport *R = new BugReport(*BT_OffsetFree[*CheckKind], os.str(), N); R->markInteresting(MR->getBaseRegion()); R->addRange(Range); C.emitReport(R); @@ -1370,18 +1389,21 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_UseFree) - BT_UseFree.reset(new BugType("Use-after-free", "Memory Error")); + if (!BT_UseFree[*CheckKind]) + BT_UseFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Use-after-free", "Memory Error")); - BugReport *R = new BugReport(*BT_UseFree, + BugReport *R = new BugReport(*BT_UseFree[*CheckKind], "Use of memory after it is freed", N); R->markInteresting(Sym); @@ -1395,21 +1417,25 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, SymbolRef Sym, SymbolRef PrevSym) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; if (ExplodedNode *N = C.generateSink()) { - if (!BT_DoubleFree) - BT_DoubleFree.reset(new BugType("Double free", "Memory Error")); - - BugReport *R = new BugReport(*BT_DoubleFree, - (Released ? "Attempt to free released memory" - : "Attempt to free non-owned memory"), - N); + if (!BT_DoubleFree[*CheckKind]) + BT_DoubleFree[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Double free", "Memory Error")); + + BugReport *R = + new BugReport(*BT_DoubleFree[*CheckKind], + (Released ? "Attempt to free released memory" + : "Attempt to free non-owned memory"), + N); R->addRange(Range); R->markInteresting(Sym); if (PrevSym) @@ -1421,15 +1447,18 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { - if (!Filter.CNewDeleteChecker) + if (!ChecksEnabled[CK_NewDeleteChecker]) return; - if (!isTrackedByCurrentChecker(C, Sym)) + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) return; + assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind"); if (ExplodedNode *N = C.generateSink()) { if (!BT_DoubleDelete) - BT_DoubleDelete.reset(new BugType("Double delete", "Memory Error")); + BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], + "Double delete", "Memory Error")); BugReport *R = new BugReport(*BT_DoubleDelete, "Attempt to delete released memory", N); @@ -1601,31 +1630,34 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const { - if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && - !Filter.CNewDeleteLeaksChecker) + if (!ChecksEnabled[CK_MallocOptimistic] && + !ChecksEnabled[CK_MallocPessimistic] && + !ChecksEnabled[CK_NewDeleteLeaksChecker]) return; const RefState *RS = C.getState()->get<RegionState>(Sym); assert(RS && "cannot leak an untracked symbol"); AllocationFamily Family = RS->getAllocationFamily(); - if (!isTrackedByCurrentChecker(Family)) + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + if (!CheckKind.hasValue()) return; // Special case for new and new[]; these are controlled by a separate checker // flag so that they can be selectively disabled. if (Family == AF_CXXNew || Family == AF_CXXNewArray) - if (!Filter.CNewDeleteLeaksChecker) + if (!ChecksEnabled[CK_NewDeleteLeaksChecker]) return; assert(N); - if (!BT_Leak) { - BT_Leak.reset(new BugType("Memory leak", "Memory Error")); + if (!BT_Leak[*CheckKind]) { + BT_Leak[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Memory leak", "Memory Error")); // Leaks should not be reported if they are post-dominated by a sink: // (1) Sinks are higher importance bugs. // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending // with __noreturn functions such as assert() or exit(). We choose not // to report leaks on such paths. - BT_Leak->setSuppressOnSink(true); + BT_Leak[*CheckKind]->setSuppressOnSink(true); } // Most bug reports are cached at the location where they occurred. @@ -1656,9 +1688,9 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, os << "Potential memory leak"; } - BugReport *R = new BugReport(*BT_Leak, os.str(), N, - LocUsedForUniqueing, - AllocNode->getLocationContext()->getDecl()); + BugReport *R = + new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); R->addVisitor(new MallocBugVisitor(Sym, true)); C.emitReport(R); @@ -1732,11 +1764,12 @@ void MallocChecker::checkPreCall(const CallEvent &Call, if (!FD) return; - if ((Filter.CMallocOptimistic || Filter.CMallocPessimistic) && + if ((ChecksEnabled[CK_MallocOptimistic] || + ChecksEnabled[CK_MallocPessimistic]) && isFreeFunction(FD, C.getASTContext())) return; - if (Filter.CNewDeleteChecker && + if (ChecksEnabled[CK_NewDeleteChecker] && isStandardNewDelete(FD, C.getASTContext())) return; } @@ -2230,17 +2263,26 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { registerCStringCheckerBasic(mgr); - mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteLeaksChecker = true; + MallocChecker *checker = mgr.registerChecker<MallocChecker>(); + checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; + checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = + mgr.getCurrentCheckName(); // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete // checker. - mgr.registerChecker<MallocChecker>()->Filter.CNewDeleteChecker = true; + if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { + checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; + checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = + mgr.getCurrentCheckName(); + } } -#define REGISTER_CHECKER(name) \ -void ento::register##name(CheckerManager &mgr) {\ - registerCStringCheckerBasic(mgr); \ - mgr.registerChecker<MallocChecker>()->Filter.C##name = true;\ -} +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + registerCStringCheckerBasic(mgr); \ + MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \ + checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ + checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } REGISTER_CHECKER(MallocPessimistic) REGISTER_CHECKER(MallocOptimistic) |