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;
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.
///
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;
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");
}
}
if (SymbolRef Sym = C.getSVal(*I).getAsSymbol())
checkUseAfterFree(Sym, C, *I);
- if (!Filter.CNewDeleteChecker && !Filter.CMismatchedDeallocatorChecker)
- return;
-
if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
return;
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;
}
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;
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);
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() << ")";
!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"));
}
}
-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);
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);
}
!Filter.CNewDeleteChecker)
return;
+ if (!isTrackedFamily(C, AllocExpr))
+ return;
+
ExplodedNode *N = C.generateSink();
if (N == NULL)
return;
!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"));
!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"));
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);