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.cpp507
1 files changed, 375 insertions, 132 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index aee5a43048b9..0cf009407349 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -43,12 +43,15 @@ enum AllocationFamily {
AF_Malloc,
AF_CXXNew,
AF_CXXNewArray,
- AF_IfNameIndex
+ AF_IfNameIndex,
+ AF_Alloca
};
class RefState {
enum Kind { // Reference to allocated memory.
Allocated,
+ // Reference to zero-allocated memory.
+ AllocatedOfSizeZero,
// Reference to released/freed memory.
Released,
// The responsibility for freeing resources has transferred from
@@ -61,8 +64,8 @@ class RefState {
};
const Stmt *S;
- unsigned K : 2; // Kind enum, but stored as a bitfield.
- unsigned Family : 30; // Rest of 32-bit word, currently just an allocation
+ unsigned K : 3; // Kind enum, but stored as a bitfield.
+ unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
// family.
RefState(Kind k, const Stmt *s, unsigned family)
@@ -71,6 +74,7 @@ class RefState {
}
public:
bool isAllocated() const { return K == Allocated; }
+ bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }
bool isReleased() const { return K == Released; }
bool isRelinquished() const { return K == Relinquished; }
bool isEscaped() const { return K == Escaped; }
@@ -86,6 +90,10 @@ public:
static RefState getAllocated(unsigned family, const Stmt *s) {
return RefState(Allocated, s, family);
}
+ static RefState getAllocatedOfSizeZero(const RefState *RS) {
+ return RefState(AllocatedOfSizeZero, RS->getStmt(),
+ RS->getAllocationFamily());
+ }
static RefState getReleased(unsigned family, const Stmt *s) {
return RefState(Released, s, family);
}
@@ -106,6 +114,7 @@ public:
switch (static_cast<Kind>(K)) {
#define CASE(ID) case ID: OS << #ID; break;
CASE(Allocated)
+ CASE(AllocatedOfSizeZero)
CASE(Released)
CASE(Relinquished)
CASE(Escaped)
@@ -160,16 +169,16 @@ class MallocChecker : public Checker<check::DeadSymbols,
{
public:
MallocChecker()
- : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr),
- II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr),
- II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr),
- II_if_nameindex(nullptr), II_if_freenameindex(nullptr) {}
+ : II_alloca(nullptr), II_malloc(nullptr), II_free(nullptr),
+ II_realloc(nullptr), II_calloc(nullptr), II_valloc(nullptr),
+ II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr),
+ II_kmalloc(nullptr), II_if_nameindex(nullptr),
+ II_if_freenameindex(nullptr) {}
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
enum CheckKind {
- CK_MallocPessimistic,
- CK_MallocOptimistic,
+ CK_MallocChecker,
CK_NewDeleteChecker,
CK_NewDeleteLeaksChecker,
CK_MismatchedDeallocatorChecker,
@@ -182,6 +191,8 @@ public:
MOK_Any
};
+ DefaultBool IsOptimistic;
+
DefaultBool ChecksEnabled[CK_NumCheckKinds];
CheckName CheckNames[CK_NumCheckKinds];
@@ -216,11 +227,14 @@ private:
mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_FreeAlloca[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
- mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
- *II_valloc, *II_reallocf, *II_strndup, *II_strdup,
- *II_kmalloc, *II_if_nameindex, *II_if_freenameindex;
+ mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+ mutable IdentifierInfo *II_alloca, *II_malloc, *II_free, *II_realloc,
+ *II_calloc, *II_valloc, *II_reallocf, *II_strndup,
+ *II_strdup, *II_kmalloc, *II_if_nameindex,
+ *II_if_freenameindex;
mutable Optional<uint64_t> KernelZeroFlagVal;
void initIdentifierInfo(ASTContext &C) const;
@@ -252,22 +266,24 @@ private:
MemoryOperationKind MemKind) const;
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
+
+ /// \brief Perform a zero-allocation check.
+ ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
+ const unsigned AllocationSizeArg,
+ ProgramStateRef State) const;
+
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
- const OwnershipAttr* Att) const;
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- const Expr *SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc) {
- return MallocMemAux(C, CE,
- State->getSVal(SizeEx, C.getLocationContext()),
- Init, State, Family);
- }
-
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc);
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- SVal SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ SVal SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
@@ -281,7 +297,8 @@ private:
AllocationFamily Family = AF_Malloc);
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr* Att) const;
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, unsigned Num,
bool Hold,
@@ -295,14 +312,19 @@ private:
bool ReturnsNullOnFailure = false) const;
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
- bool FreesMemOnFailure) const;
- static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
+ bool FreesMemOnFailure,
+ ProgramStateRef State) const;
+ static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State);
///\brief Check if the memory associated with this symbol was released.
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
+ void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const;
+
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
/// Check if the function is known free memory, or if it is
@@ -330,15 +352,20 @@ private:
/// Tells if a given family/call/symbol is tracked by the current checker.
/// Sets CheckKind to the kind of the checker responsible for this
/// family/call/symbol.
- Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const;
+ Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck = false) const;
Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const;
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck = false) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck = false) 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 ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const;
void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
const Expr *DeallocExpr, const RefState *RS,
SymbolRef Sym, bool OwnershipTransferred) const;
@@ -352,6 +379,9 @@ private:
void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
+ void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const;
+
/// Find the location of the allocation for Sym on the path leading to the
/// exploded node N.
LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
@@ -384,7 +414,7 @@ private:
MallocBugVisitor(SymbolRef S, bool isLeak = false)
: Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {}
- virtual ~MallocBugVisitor() {}
+ ~MallocBugVisitor() override {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
@@ -396,7 +426,9 @@ private:
const Stmt *Stmt) {
// Did not track -> allocated. Other state (released) -> allocated.
return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
- (S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated()));
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (!SPrev || !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
}
inline bool isReleased(const RefState *S, const RefState *SPrev,
@@ -422,7 +454,9 @@ private:
// check. If we have to handle more cases here, it might be cleaner just
// to track this extra bit in the state itself.
return ((!Stmt || !isa<CallExpr>(Stmt)) &&
- (S && S->isAllocated()) && (SPrev && !SPrev->isAllocated()));
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (SPrev && !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
}
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
@@ -497,6 +531,7 @@ public:
void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (II_malloc)
return;
+ II_alloca = &Ctx.Idents.get("alloca");
II_malloc = &Ctx.Idents.get("malloc");
II_free = &Ctx.Idents.get("free");
II_realloc = &Ctx.Idents.get("realloc");
@@ -517,6 +552,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
return true;
+ if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return true;
+
if (isStandardNewDelete(FD, C))
return true;
@@ -560,12 +598,17 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
if (FunI == II_if_nameindex)
return true;
}
+
+ if (Family == AF_Alloca && CheckAlloc) {
+ if (FunI == II_alloca)
+ return true;
+ }
}
if (Family != AF_Malloc)
return false;
- if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs()) {
+ if (IsOptimistic && FD->hasAttrs()) {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
@@ -712,6 +755,8 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
if (CE->getNumArgs() < 3) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ if (CE->getNumArgs() == 1)
+ State = ProcessZeroAllocation(C, CE, 0, State);
} else if (CE->getNumArgs() == 3) {
llvm::Optional<ProgramStateRef> MaybeState =
performKernelMalloc(CE, C, State);
@@ -731,31 +776,43 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
} else if (FunI == II_realloc) {
- State = ReallocMem(C, CE, false);
+ State = ReallocMem(C, CE, false, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true);
+ State = ReallocMem(C, CE, true, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_calloc) {
- State = CallocMem(C, CE);
+ State = CallocMem(C, CE, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
} 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);
- }
- else if (isStandardNewDelete(FD, C.getASTContext())) {
+ } else if (FunI == II_alloca) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_Alloca);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } 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)
+ if (K == OO_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNew);
- else if (K == OO_Array_New)
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
+ else if (K == OO_Array_New) {
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNewArray);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
else if (K == OO_Delete || K == OO_Array_Delete)
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
else
@@ -770,19 +827,18 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
}
}
- if (ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
switch (I->getOwnKind()) {
case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, I);
+ State = MallocMemReturnsAttr(C, CE, I, State);
break;
case OwnershipAttr::Takes:
case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, I);
+ State = FreeMemAttr(C, CE, I, State);
break;
}
}
@@ -790,6 +846,68 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
C.addTransition(State);
}
+// Performs a 0-sized allocations check.
+ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
+ const Expr *E,
+ const unsigned AllocationSizeArg,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
+ const Expr *Arg = nullptr;
+
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ Arg = CE->getArg(AllocationSizeArg);
+ }
+ else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+ if (NE->isArray())
+ Arg = NE->getArraySize();
+ else
+ return State;
+ }
+ else
+ llvm_unreachable("not a CallExpr or CXXNewExpr");
+
+ assert(Arg);
+
+ Optional<DefinedSVal> DefArgVal =
+ State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>();
+
+ if (!DefArgVal)
+ return State;
+
+ // Check if the allocation size is 0.
+ ProgramStateRef TrueState, FalseState;
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ DefinedSVal Zero =
+ SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
+
+ std::tie(TrueState, FalseState) =
+ State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
+
+ if (TrueState && !FalseState) {
+ SVal retVal = State->getSVal(E, C.getLocationContext());
+ SymbolRef Sym = retVal.getAsLocSymbol();
+ if (!Sym)
+ return State;
+
+ const RefState *RS = State->get<RegionState>(Sym);
+ if (!RS)
+ return State; // TODO: change to assert(RS); after realloc() will
+ // guarantee have a RegionState attached.
+
+ if (!RS->isAllocated())
+ return State;
+
+ return TrueState->set<RegionState>(Sym,
+ RefState::getAllocatedOfSizeZero(RS));
+ }
+
+ // Assume the value is non-zero going forward.
+ assert(FalseState);
+ return FalseState;
+}
+
static QualType getDeepPointeeType(QualType T) {
QualType Result = T, PointeeType = T->getPointeeType();
while (!PointeeType.isNull()) {
@@ -849,6 +967,7 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
// existing binding.
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
: AF_CXXNew);
+ State = ProcessZeroAllocation(C, NE, 0, State);
C.addTransition(State);
}
@@ -919,15 +1038,31 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
ProgramStateRef
MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr *Att) const {
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (Att->getModule() != II_malloc)
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
+ return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State);
}
- return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState());
+ return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
+}
+
+ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
+ return MallocMemAux(C, CE, State->getSVal(SizeEx, C.getLocationContext()),
+ Init, State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -935,6 +1070,8 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
SVal Size, SVal Init,
ProgramStateRef State,
AllocationFamily Family) {
+ if (!State)
+ return nullptr;
// We expect the malloc functions to return a pointer.
if (!Loc::isLocType(CE->getType()))
@@ -976,6 +1113,9 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
const Expr *E,
ProgramStateRef State,
AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
// Get the return value.
SVal retVal = State->getSVal(E, C.getLocationContext());
@@ -992,11 +1132,14 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
const CallExpr *CE,
- const OwnershipAttr *Att) const {
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (Att->getModule() != II_malloc)
return nullptr;
- ProgramStateRef State = C.getState();
bool ReleasedAllocated = false;
for (const auto &Arg : Att->args()) {
@@ -1011,15 +1154,18 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const CallExpr *CE,
- ProgramStateRef state,
+ ProgramStateRef State,
unsigned Num,
bool Hold,
bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < (Num + 1))
return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, state, Hold,
+ return FreeMemAux(C, CE->getArg(Num), CE, State, Hold,
ReleasedAllocated, ReturnsNullOnFailure);
}
@@ -1065,6 +1211,9 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
return AF_IfNameIndex;
+ if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return AF_Alloca;
+
return AF_None;
}
@@ -1129,6 +1278,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
case AF_CXXNew: os << "'new'"; return;
case AF_CXXNewArray: os << "'new[]'"; return;
case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+ case AF_Alloca:
case AF_None: llvm_unreachable("not a deallocation expression");
}
}
@@ -1140,7 +1290,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os,
case AF_CXXNew: os << "'delete'"; return;
case AF_CXXNewArray: os << "'delete[]'"; return;
case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
- case AF_None: llvm_unreachable("suspicious AF_None argument");
+ case AF_Alloca:
+ case AF_None: llvm_unreachable("suspicious argument");
}
}
@@ -1152,6 +1303,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
bool &ReleasedAllocated,
bool ReturnsNullOnFailure) const {
+ if (!State)
+ return nullptr;
+
SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());
if (!ArgVal.getAs<DefinedOrUnknownSVal>())
return nullptr;
@@ -1191,8 +1345,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const MemSpaceRegion *MS = R->getMemorySpace();
- // Parameters, locals, statics, globals, and memory returned by alloca()
- // shouldn't be freed.
+ // Parameters, locals, statics, globals, and memory returned by
+ // __builtin_alloca() shouldn't be freed.
if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
// FIXME: at the time this code was written, malloc() regions were
// represented by conjured symbols, which are all in UnknownSpaceRegion.
@@ -1201,8 +1355,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Of course, free() can work on memory allocated outside the current
// function, so UnknownSpaceRegion is always a possibility.
// False negatives are better than false positives.
-
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+
+ if (isa<AllocaRegion>(R))
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ else
+ ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+
return nullptr;
}
@@ -1218,6 +1376,12 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (RsBase) {
+ // Memory returned by alloca() shouldn't be freed.
+ if (RsBase->getAllocationFamily() == AF_Alloca) {
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ return nullptr;
+ }
+
// Check for double free first.
if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
!didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
@@ -1227,7 +1391,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// If the pointer is allocated or escaped, but we are now trying to free it,
// check that the call to free is proper.
- } else if (RsBase->isAllocated() || RsBase->isEscaped()) {
+ } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||
+ RsBase->isEscaped()) {
// Check if an expected deallocation function matches the real one.
bool DeallocMatchesAlloc =
@@ -1252,7 +1417,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
}
- ReleasedAllocated = (RsBase != nullptr) && RsBase->isAllocated();
+ ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
+ RsBase->isAllocatedOfSizeZero());
// Clean out the info on previous call to free return info.
State = State->remove<FreeReturnValue>(SymBase);
@@ -1281,21 +1447,26 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
Optional<MallocChecker::CheckKind>
-MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
+MallocChecker::getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck) const {
switch (Family) {
case AF_Malloc:
+ case AF_Alloca:
case AF_IfNameIndex: {
- if (ChecksEnabled[CK_MallocOptimistic]) {
- return CK_MallocOptimistic;
- } else if (ChecksEnabled[CK_MallocPessimistic]) {
- return CK_MallocPessimistic;
- }
+ if (ChecksEnabled[CK_MallocChecker])
+ return CK_MallocChecker;
+
return Optional<MallocChecker::CheckKind>();
}
case AF_CXXNew:
case AF_CXXNewArray: {
- if (ChecksEnabled[CK_NewDeleteChecker]) {
- return CK_NewDeleteChecker;
+ if (IsALeakCheck) {
+ if (ChecksEnabled[CK_NewDeleteLeaksChecker])
+ return CK_NewDeleteLeaksChecker;
+ }
+ else {
+ if (ChecksEnabled[CK_NewDeleteChecker])
+ return CK_NewDeleteChecker;
}
return Optional<MallocChecker::CheckKind>();
}
@@ -1308,16 +1479,18 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
Optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const {
- return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt));
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck) const {
+ return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt),
+ IsALeakCheck);
}
Optional<MallocChecker::CheckKind>
-MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const {
-
+MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck) const {
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS);
- return getCheckIfTracked(RS->getAllocationFamily());
+ return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck);
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -1411,8 +1584,7 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
SourceRange Range,
const Expr *DeallocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1433,23 +1605,19 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
MR = ER->getSuperRegion();
- if (MR && isa<AllocaRegion>(MR))
- os << "Memory allocated by alloca() should not be deallocated";
- else {
- os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
- os << "deallocator";
-
- os << " is ";
- bool Summarized = MR ? SummarizeRegion(os, MR)
- : SummarizeValue(os, ArgVal);
- if (Summarized)
- os << ", which is not memory allocated by ";
- else
- os << "not memory allocated by ";
+ os << "Argument to ";
+ if (!printAllocDeallocName(os, C, DeallocExpr))
+ os << "deallocator";
- printExpectedAllocName(os, C, DeallocExpr);
- }
+ os << " is ";
+ bool Summarized = MR ? SummarizeRegion(os, MR)
+ : SummarizeValue(os, ArgVal);
+ if (Summarized)
+ os << ", which is not memory allocated by ";
+ else
+ os << "not memory allocated by ";
+
+ printExpectedAllocName(os, C, DeallocExpr);
BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);
R->markInteresting(MR);
@@ -1458,6 +1626,31 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
}
}
+void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const {
+
+ Optional<MallocChecker::CheckKind> CheckKind;
+
+ if (ChecksEnabled[CK_MallocChecker])
+ CheckKind = CK_MallocChecker;
+ else if (ChecksEnabled[CK_MismatchedDeallocatorChecker])
+ CheckKind = CK_MismatchedDeallocatorChecker;
+ else
+ return;
+
+ if (ExplodedNode *N = C.generateSink()) {
+ if (!BT_FreeAlloca[*CheckKind])
+ BT_FreeAlloca[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error"));
+
+ BugReport *R = new BugReport(*BT_FreeAlloca[*CheckKind],
+ "Memory allocated by alloca() should not be deallocated", N);
+ R->markInteresting(ArgVal.getAsRegion());
+ R->addRange(Range);
+ C.emitReport(R);
+ }
+}
+
void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
SourceRange Range,
const Expr *DeallocExpr,
@@ -1517,8 +1710,8 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
SourceRange Range, const Expr *DeallocExpr,
const Expr *AllocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1573,8 +1766,7 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
SymbolRef Sym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1601,8 +1793,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
bool Released, SymbolRef Sym,
SymbolRef PrevSym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteChecker])
return;
@@ -1637,7 +1828,6 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
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)
@@ -1653,16 +1843,49 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
}
}
+void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
+ SourceRange Range,
+ SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateSink()) {
+ if (!BT_UseZerroAllocated[*CheckKind])
+ BT_UseZerroAllocated[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Use of zero allocated", "Memory Error"));
+
+ BugReport *R = new BugReport(*BT_UseZerroAllocated[*CheckKind],
+ "Use of zero-allocated memory", N);
+
+ R->addRange(Range);
+ if (Sym) {
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ }
+ C.emitReport(R);
+ }
+}
+
ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
const CallExpr *CE,
- bool FreesOnFail) const {
+ bool FreesOnFail,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < 2)
return nullptr;
- ProgramStateRef state = C.getState();
const Expr *arg0Expr = CE->getArg(0);
const LocationContext *LCtx = C.getLocationContext();
- SVal Arg0Val = state->getSVal(arg0Expr, LCtx);
+ SVal Arg0Val = State->getSVal(arg0Expr, LCtx);
if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
@@ -1670,7 +1893,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
SValBuilder &svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal PtrEQ =
- svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull());
+ svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
// Get the size argument. If there is no size arg then give up.
const Expr *Arg1 = CE->getArg(1);
@@ -1678,20 +1901,20 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
return nullptr;
// Get the value of the size argument.
- SVal Arg1ValG = state->getSVal(Arg1, LCtx);
+ SVal Arg1ValG = State->getSVal(Arg1, LCtx);
if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();
// Compare the size argument to 0.
DefinedOrUnknownSVal SizeZero =
- svalBuilder.evalEQ(state, Arg1Val,
+ svalBuilder.evalEQ(State, Arg1Val,
svalBuilder.makeIntValWithPtrWidth(0, false));
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
- std::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
+ std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ);
ProgramStateRef StateSizeIsZero, StateSizeNotZero;
- std::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero);
+ std::tie(StateSizeIsZero, StateSizeNotZero) = State->assume(SizeZero);
// We only assume exceptional states if they are definitely true; if the
// state is under-constrained, assume regular realloc behavior.
bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
@@ -1711,7 +1934,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
// Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
assert(!PrtIsNull);
SymbolRef FromPtr = arg0Val.getAsSymbol();
- SVal RetVal = state->getSVal(CE, LCtx);
+ SVal RetVal = State->getSVal(CE, LCtx);
SymbolRef ToPtr = RetVal.getAsSymbol();
if (!FromPtr || !ToPtr)
return nullptr;
@@ -1731,7 +1954,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
// Default behavior.
if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) {
+ FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
UnknownVal(), stateFree);
@@ -1755,20 +1978,23 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
return nullptr;
}
-ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE){
+ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) {
+ if (!State)
+ return nullptr;
+
if (CE->getNumArgs() < 2)
return nullptr;
- ProgramStateRef state = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
const LocationContext *LCtx = C.getLocationContext();
- SVal count = state->getSVal(CE->getArg(0), LCtx);
- SVal elementSize = state->getSVal(CE->getArg(1), LCtx);
- SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize,
+ SVal count = State->getSVal(CE->getArg(0), LCtx);
+ SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
+ SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
svalBuilder.getContext().getSizeType());
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- return MallocMemAux(C, CE, TotalSize, zeroVal, state);
+ return MallocMemAux(C, CE, TotalSize, zeroVal, State);
}
LeakInfo
@@ -1801,9 +2027,11 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
}
}
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
+ // Allocation node, is the last node in the current or parent context in
+ // which the symbol was tracked.
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
AllocNode = N;
N = N->pred_empty() ? nullptr : *(N->pred_begin());
}
@@ -1814,23 +2042,22 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
CheckerContext &C) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
+ if (!ChecksEnabled[CK_MallocChecker] &&
!ChecksEnabled[CK_NewDeleteLeaksChecker])
return;
const RefState *RS = C.getState()->get<RegionState>(Sym);
assert(RS && "cannot leak an untracked symbol");
AllocationFamily Family = RS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
+
+ if (Family == AF_Alloca)
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 (!ChecksEnabled[CK_NewDeleteLeaksChecker])
- return;
+ Optional<MallocChecker::CheckKind>
+ CheckKind = getCheckIfTracked(Family, true);
+
+ if (!CheckKind.hasValue())
+ return;
assert(N);
if (!BT_Leak[*CheckKind]) {
@@ -1893,7 +2120,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
SmallVector<SymbolRef, 2> Errors;
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
if (SymReaper.isDead(I->first)) {
- if (I->second.isAllocated())
+ if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero())
Errors.push_back(I->first);
// Remove the dead symbol from the map.
RS = F.remove(RS, I->first);
@@ -1949,8 +2176,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
return;
ASTContext &Ctx = C.getASTContext();
- if ((ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MallocPessimistic]) &&
+ if (ChecksEnabled[CK_MallocChecker] &&
(isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||
isCMemFunction(FD, Ctx, AF_IfNameIndex,
MemoryOperationKind::MOK_Free)))
@@ -2062,6 +2288,15 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
return false;
}
+void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const {
+ assert(Sym);
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+
+ if (RS && RS->isAllocatedOfSizeZero())
+ ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym);
+}
+
bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
if (isReleased(Sym, C)) {
@@ -2075,8 +2310,10 @@ bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const {
SymbolRef Sym = l.getLocSymbolInBase();
- if (Sym)
+ if (Sym) {
checkUseAfterFree(Sym, C, S);
+ checkUseZeroAllocated(Sym, C, S);
+ }
}
// If a symbolic region is assumed to NULL (or another constant), stop tracking
@@ -2320,7 +2557,8 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
continue;
if (const RefState *RS = State->get<RegionState>(sym)) {
- if (RS->isAllocated() && CheckRefState(RS)) {
+ if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
+ CheckRefState(RS)) {
State = State->remove<RegionState>(sym);
State = State->set<RegionState>(sym, RefState::getEscaped(RS));
}
@@ -2443,6 +2681,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
const RefState *RefS = State->get<RegionState>(I.getKey());
AllocationFamily Family = RefS->getAllocationFamily();
Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind.hasValue())
+ CheckKind = getCheckIfTracked(Family, true);
I.getKey()->dumpToStream(Out);
Out << " : ";
@@ -2457,6 +2697,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption(
+ "Optimistic", false, checker);
checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
mgr.getCurrentCheckName();
@@ -2470,11 +2712,12 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
void ento::register##name(CheckerManager &mgr) { \
registerCStringCheckerBasic(mgr); \
MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getBooleanOption( \
+ "Optimistic", false, checker); \
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
}
-REGISTER_CHECKER(MallocPessimistic)
-REGISTER_CHECKER(MallocOptimistic)
+REGISTER_CHECKER(MallocChecker)
REGISTER_CHECKER(NewDeleteChecker)
REGISTER_CHECKER(MismatchedDeallocatorChecker)