aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp226
1 files changed, 137 insertions, 89 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 57666499c4..f686735010 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -149,7 +149,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
mutable OwningPtr<BugType> BT_Leak;
mutable OwningPtr<BugType> BT_UseFree;
mutable OwningPtr<BugType> BT_BadFree;
- mutable OwningPtr<BugType> BT_BadDealloc;
+ 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;
@@ -198,7 +198,7 @@ private:
void initIdentifierInfo(ASTContext &C) const;
/// \brief Determine family of a deallocation expression.
- AllocationFamily getAllocationFamily(CheckerContext &C, const Expr *E) const;
+ AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const;
/// \brief Print names of allocators and deallocators.
///
@@ -282,12 +282,19 @@ private:
PointerEscapeKind Kind,
bool(*CheckRefState)(const RefState*)) const;
+ // Used to suppress warnings if they are not related to the tracked family
+ // (derived from AllocDeallocStmt).
+ bool isTrackedFamily(AllocationFamily Family) const;
+ bool isTrackedFamily(CheckerContext &C, const Stmt *AllocDeallocStmt) const;
+ bool isTrackedFamily(CheckerContext &C, SymbolRef Sym) const;
+
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr) const;
- void ReportBadDealloc(CheckerContext &C, SourceRange Range,
- const Expr *DeallocExpr, const RefState *RS) const;
+ void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
+ const Expr *DeallocExpr,
+ const RefState *RS) const;
void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr,
const Expr *AllocExpr = 0) const;
@@ -558,45 +565,39 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
- if (Filter.CMallocOptimistic || Filter.CMallocPessimistic ||
- Filter.CMismatchedDeallocatorChecker) {
- if (FunI == II_malloc || FunI == II_valloc) {
- if (CE->getNumArgs() < 1)
- return;
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == II_realloc) {
- State = ReallocMem(C, CE, false);
- } else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true);
- } else if (FunI == II_calloc) {
- State = CallocMem(C, CE);
- } else if (FunI == II_free) {
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- } else if (FunI == II_strdup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == II_strndup) {
- State = MallocUpdateRefState(C, CE, State);
- }
+ if (FunI == II_malloc || FunI == II_valloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (FunI == II_realloc) {
+ State = ReallocMem(C, CE, false);
+ } else if (FunI == II_reallocf) {
+ State = ReallocMem(C, CE, true);
+ } else if (FunI == II_calloc) {
+ State = CallocMem(C, CE);
+ } else if (FunI == II_free) {
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
+ } else if (FunI == II_strdup) {
+ State = MallocUpdateRefState(C, CE, State);
+ } else if (FunI == II_strndup) {
+ State = MallocUpdateRefState(C, CE, State);
}
-
- if (Filter.CNewDeleteChecker || Filter.CMismatchedDeallocatorChecker) {
- if (isStandardNewDelete(FD, C.getASTContext())) {
- // Process direct calls to operator new/new[]/delete/delete[] functions
- // as distinct from new/new[]/delete/delete[] expressions that are
- // processed by the checkPostStmt callbacks for CXXNewExpr and
- // CXXDeleteExpr.
- OverloadedOperatorKind K = FD->getOverloadedOperator();
- if (K == OO_New)
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNew);
- else if (K == OO_Array_New)
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNewArray);
- else if (K == OO_Delete || K == OO_Array_Delete)
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- else
- llvm_unreachable("not a new/delete operator");
- }
+ else if (isStandardNewDelete(FD, C.getASTContext())) {
+ // Process direct calls to operator new/new[]/delete/delete[] functions
+ // as distinct from new/new[]/delete/delete[] expressions that are
+ // processed by the checkPostStmt callbacks for CXXNewExpr and
+ // CXXDeleteExpr.
+ OverloadedOperatorKind K = FD->getOverloadedOperator();
+ if (K == OO_New)
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNew);
+ else if (K == OO_Array_New)
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNewArray);
+ else if (K == OO_Delete || K == OO_Array_Delete)
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
+ else
+ llvm_unreachable("not a new/delete operator");
}
}
@@ -631,9 +632,6 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
if (SymbolRef Sym = C.getSVal(*I).getAsSymbol())
checkUseAfterFree(Sym, C, *I);
- if (!Filter.CNewDeleteChecker && !Filter.CMismatchedDeallocatorChecker)
- return;
-
if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
return;
@@ -654,9 +652,6 @@ void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
checkUseAfterFree(Sym, C, DE->getArgument());
- if (!Filter.CNewDeleteChecker && !Filter.CMismatchedDeallocatorChecker)
- return;
-
if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
return;
@@ -838,32 +833,39 @@ static bool didPreviousFreeFail(ProgramStateRef State,
}
AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
- const Expr *E) const {
- if (!E)
+ const Stmt *S) const {
+ if (!S)
return AF_None;
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
const FunctionDecl *FD = C.getCalleeDecl(CE);
+
+ if (!FD)
+ FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
+
ASTContext &Ctx = C.getASTContext();
- if (isFreeFunction(FD, Ctx))
+ if (isAllocationFunction(FD, Ctx) || isFreeFunction(FD, Ctx))
return AF_Malloc;
if (isStandardNewDelete(FD, Ctx)) {
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
- if (Kind == OO_Delete)
+ if (Kind == OO_New || Kind == OO_Delete)
return AF_CXXNew;
- else if (Kind == OO_Array_Delete)
+ else if (Kind == OO_Array_New || Kind == OO_Array_Delete)
return AF_CXXNewArray;
}
return AF_None;
}
- if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E))
+ if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S))
+ return NE->isArray() ? AF_CXXNewArray : AF_CXXNew;
+
+ if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S))
return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew;
- if (isa<ObjCMessageExpr>(E))
+ if (isa<ObjCMessageExpr>(S))
return AF_Malloc;
return AF_None;
@@ -1003,34 +1005,39 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const RefState *RsBase = State->get<RegionState>(SymBase);
SymbolRef PreviousRetStatusSymbol = 0;
- // Check double free.
- if (RsBase &&
- (RsBase->isReleased() || RsBase->isRelinquished()) &&
- !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
- ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
- SymBase, PreviousRetStatusSymbol);
- return 0;
- }
+ if (RsBase) {
- // Check if an expected deallocation function matches the real one.
- if (RsBase &&
- RsBase->getAllocationFamily() != AF_None &&
- RsBase->getAllocationFamily() != getAllocationFamily(C, ParentExpr) ) {
- ReportBadDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase);
- return 0;
- }
+ bool DeallocMatchesAlloc =
+ RsBase->getAllocationFamily() == AF_None ||
+ RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
- // Check if the memory location being freed is the actual location
- // allocated, or an offset.
- RegionOffset Offset = R->getAsOffset();
- if (RsBase && RsBase->isAllocated() &&
- Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
- Offset.getOffset() != 0) {
- const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
- ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
- AllocExpr);
- return 0;
+ // Check if an expected deallocation function matches the real one.
+ if (!DeallocMatchesAlloc && RsBase->isAllocated()) {
+ ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase);
+ return 0;
+ }
+
+ // Check double free.
+ if (DeallocMatchesAlloc &&
+ (RsBase->isReleased() || RsBase->isRelinquished()) &&
+ !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
+ ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
+ SymBase, PreviousRetStatusSymbol);
+ return 0;
+ }
+
+ // Check if the memory location being freed is the actual location
+ // allocated, or an offset.
+ RegionOffset Offset = R->getAsOffset();
+ if (RsBase->isAllocated() &&
+ Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() != 0) {
+ const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
+ ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ AllocExpr);
+ return 0;
+ }
}
ReleasedAllocated = (RsBase != 0);
@@ -1060,6 +1067,30 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
RefState::getReleased(Family, ParentExpr));
}
+bool MallocChecker::isTrackedFamily(AllocationFamily Family) const {
+ if (Family == AF_Malloc &&
+ (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic))
+ return false;
+
+ if ((Family == AF_CXXNew || Family == AF_CXXNewArray) &&
+ !Filter.CNewDeleteChecker)
+ return false;
+
+ return true;
+}
+
+bool MallocChecker::isTrackedFamily(CheckerContext &C,
+ const Stmt *AllocDeallocStmt) const {
+ return isTrackedFamily(getAllocationFamily(C, AllocDeallocStmt));
+}
+
+bool MallocChecker::isTrackedFamily(CheckerContext &C, SymbolRef Sym) const {
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+
+ return RS ? isTrackedFamily(RS->getAllocationFamily())
+ : isTrackedFamily(AF_None);
+}
+
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>())
os << "an integer (" << IntVal->getValue() << ")";
@@ -1155,6 +1186,9 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
!Filter.CNewDeleteChecker)
return;
+ if (!isTrackedFamily(C, DeallocExpr))
+ return;
+
if (ExplodedNode *N = C.generateSink()) {
if (!BT_BadFree)
BT_BadFree.reset(new BugType("Bad free", "Memory Error"));
@@ -1191,16 +1225,18 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
}
}
-void MallocChecker::ReportBadDealloc(CheckerContext &C, SourceRange Range,
- const Expr *DeallocExpr,
- const RefState *RS) const {
+void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
+ SourceRange Range,
+ const Expr *DeallocExpr,
+ const RefState *RS) const {
if (!Filter.CMismatchedDeallocatorChecker)
return;
if (ExplodedNode *N = C.generateSink()) {
- if (!BT_BadDealloc)
- BT_BadDealloc.reset(new BugType("Bad deallocator", "Memory Error"));
+ if (!BT_MismatchedDealloc)
+ BT_MismatchedDealloc.reset(new BugType("Bad deallocator",
+ "Memory Error"));
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -1221,7 +1257,7 @@ void MallocChecker::ReportBadDealloc(CheckerContext &C, SourceRange Range,
if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
os << ", not " << DeallocOs.str();
- BugReport *R = new BugReport(*BT_BadDealloc, os.str(), N);
+ BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N);
R->addRange(Range);
C.emitReport(R);
}
@@ -1235,6 +1271,9 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
!Filter.CNewDeleteChecker)
return;
+ if (!isTrackedFamily(C, AllocExpr))
+ return;
+
ExplodedNode *N = C.generateSink();
if (N == NULL)
return;
@@ -1284,6 +1323,9 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
!Filter.CNewDeleteChecker)
return;
+ if (!isTrackedFamily(C, Sym))
+ return;
+
if (ExplodedNode *N = C.generateSink()) {
if (!BT_UseFree)
BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
@@ -1306,6 +1348,9 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
!Filter.CNewDeleteChecker)
return;
+ if (!isTrackedFamily(C, Sym))
+ return;
+
if (ExplodedNode *N = C.generateSink()) {
if (!BT_DoubleFree)
BT_DoubleFree.reset(new BugType("Double free", "Memory Error"));
@@ -1507,10 +1552,13 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
AllocationStmt = Exit->getCalleeContext()->getCallSite();
else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>())
AllocationStmt = SP->getStmt();
- if (AllocationStmt)
+ if (AllocationStmt) {
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
C.getSourceManager(),
AllocNode->getLocationContext());
+ if (!isTrackedFamily(C, AllocationStmt))
+ return;
+ }
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);