#include "AllocationState.h"
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Lexer.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include <climits>
#include <functional>
/// placement operators and other standard overloads.
static bool isStandardNewDelete(const FunctionDecl *FD);
static bool isStandardNewDelete(const CallEvent &Call) {
- if (!Call.getDecl())
+ if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))
return false;
return isStandardNewDelete(cast<FunctionDecl>(Call.getDecl()));
}
: public Checker<check::DeadSymbols, check::PointerEscape,
check::ConstPointerEscape, check::PreStmt<ReturnStmt>,
check::EndFunction, check::PreCall, check::PostCall,
- check::PostStmt<CXXNewExpr>, check::NewAllocator,
- check::PostStmt<BlockExpr>, check::PostObjCMessage,
- check::Location, eval::Assume> {
+ check::NewAllocator, check::PostStmt<BlockExpr>,
+ check::PostObjCMessage, check::Location, eval::Assume> {
public:
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
- void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
#define CHECK_FN(NAME) \
- void NAME(CheckerContext &C, const CallExpr *CE, ProgramStateRef State) const;
+ void NAME(const CallEvent &Call, CheckerContext &C) const;
CHECK_FN(checkFree)
CHECK_FN(checkIfNameIndex)
CHECK_FN(checkReallocN)
CHECK_FN(checkOwnershipAttr)
- void checkRealloc(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State, bool ShouldFreeOnFail) const;
+ void checkRealloc(const CallEvent &Call, CheckerContext &C,
+ bool ShouldFreeOnFail) const;
- using CheckFn =
- std::function<void(const MallocChecker *, CheckerContext &C,
- const CallExpr *CE, ProgramStateRef State)>;
+ using CheckFn = std::function<void(const MallocChecker *,
+ const CallEvent &Call, CheckerContext &C)>;
const CallDescriptionMap<CheckFn> FreeingMemFnMap{
{{"free", 1}, &MallocChecker::checkFree},
CallDescriptionMap<CheckFn> ReallocatingMemFnMap{
{{"realloc", 2},
- std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
{{"reallocf", 2},
- std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, true)},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)},
{{"g_realloc", 2},
- std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
{{"g_try_realloc", 2},
- std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
+ std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
{{"g_realloc_n", 3}, &MallocChecker::checkReallocN},
{{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN},
};
/// Process C++ operator new()'s allocation, which is the part of C++
/// new-expression that goes before the constructor.
- void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
- SVal Target, AllocationFamily Family) const;
+ LLVM_NODISCARD
+ ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call,
+ CheckerContext &C,
+ AllocationFamily Family) const;
/// Perform a zero-allocation check.
///
/// 0.
/// \param [in] RetVal Specifies the newly allocated pointer value;
/// if unspecified, the value of expression \p E is used.
- static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E,
+ LLVM_NODISCARD
+ static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call,
const unsigned IndexOfSizeArg,
ProgramStateRef State,
Optional<SVal> RetVal = None);
/// \param [in] Att The ownership_returns attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr* Att,
+ LLVM_NODISCARD
+ ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att,
ProgramStateRef State) const;
/// Models memory allocation.
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family);
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
SVal Size, SVal Init,
ProgramStateRef State,
AllocationFamily Family);
+ LLVM_NODISCARD
static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
ProgramStateRef State, SVal Target);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
+ LLVM_NODISCARD
llvm::Optional<ProgramStateRef>
- performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+ performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const;
/// Model functions with the ownership_takes and ownership_holds attributes.
/// \param [in] Att The ownership_takes or ownership_holds attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after deallocation.
- ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr* Att,
+ LLVM_NODISCARD
+ ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att,
ProgramStateRef State) const;
/// Models memory deallocation.
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
- ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call,
ProgramStateRef State, unsigned Num, bool Hold,
bool &IsKnownToBeAllocated,
AllocationFamily Family,
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
+ LLVM_NODISCARD
ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
- const Expr *ParentExpr, ProgramStateRef State,
+ const CallEvent &Call, ProgramStateRef State,
bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family,
bool ReturnsNullOnFailure = false) const;
/// \param [in] SuffixWithN Whether the reallocation function we're modeling
/// has an '_n' suffix, such as g_realloc_n.
/// \returns The ProgramState right after reallocation.
- ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call,
bool ShouldFreeOnFail, ProgramStateRef State,
AllocationFamily Family,
bool SuffixWithN = false) const;
/// \param [in] Blocks The amount of blocks that needs to be allocated.
/// \param [in] BlockBytes The size of a block.
/// \returns The symbolic value of \p Blocks * \p BlockBytes.
+ LLVM_NODISCARD
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
const Expr *BlockBytes);
/// \param [in] CE The expression that reallocated memory
/// \param [in] State The \c ProgramState right before reallocation.
/// \returns The ProgramState right after allocation.
- static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
+ LLVM_NODISCARD
+ static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call,
ProgramStateRef State);
/// See if deallocation happens in a suspicious context. If so, escape the
/// pointers that otherwise would have been deallocated and return true.
- bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE,
+ bool suppressDeallocationsInSuspiciousContexts(const CallEvent &Call,
CheckerContext &C) const;
/// If in \p S \p Sym is used, check whether \p Sym was already freed.
SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
+ LLVM_NODISCARD
ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
}
llvm::Optional<ProgramStateRef>
-MallocChecker::performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const {
// 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
//
// We treat the last argument as the flags argument, and callers fall-back to
// normal malloc on a None return. This works for the FreeBSD kernel malloc
// as well as Linux kmalloc.
- if (CE->getNumArgs() < 2)
+ if (Call.getNumArgs() < 2)
return None;
- const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
+ const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1);
const SVal V = C.getSVal(FlagsEx);
if (!V.getAs<NonLoc>()) {
// The case where 'V' can be a location can only be due to a bad header,
// If M_ZERO is set, treat this like calloc (initialized).
if (TrueState && !FalseState) {
SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
- return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState, AF_Malloc);
+ return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState,
+ AF_Malloc);
}
return None;
return TotalSize;
}
-void MallocChecker::checkBasicAlloc(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
+void MallocChecker::checkBasicAlloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
-void MallocChecker::checkKernelMalloc(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkKernelMalloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
llvm::Optional<ProgramStateRef> MaybeState =
- performKernelMalloc(CE, C, State);
+ performKernelMalloc(Call, C, State);
if (MaybeState.hasValue())
State = MaybeState.getValue();
else
- State =
- MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc);
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Malloc);
C.addTransition(State);
}
-void MallocChecker::checkRealloc(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State,
+void MallocChecker::checkRealloc(const CallEvent &Call,
+ CheckerContext &C,
bool ShouldFreeOnFail) const {
- State = ReallocMemAux(C, CE, ShouldFreeOnFail, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
+ ProgramStateRef State = C.getState();
+ State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
-void MallocChecker::checkCalloc(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
- State = CallocMem(C, CE, State);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
+void MallocChecker::checkCalloc(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = CallocMem(C, Call, State);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
-void MallocChecker::checkFree(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
bool IsKnownToBeAllocatedMemory = false;
- if (suppressDeallocationsInSuspiciousContexts(CE, C))
+ if (suppressDeallocationsInSuspiciousContexts(Call, C))
return;
- State =
- FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, AF_Malloc);
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
+ AF_Malloc);
C.addTransition(State);
}
-void MallocChecker::checkAlloca(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
+void MallocChecker::checkAlloca(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
+ AF_Alloca);
+ State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
-void MallocChecker::checkStrdup(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkStrdup(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
State = MallocUpdateRefState(C, CE, State, AF_Malloc);
C.addTransition(State);
}
-void MallocChecker::checkIfNameIndex(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkIfNameIndex(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
// Should we model this differently? We can allocate a fixed number of
// elements with zeros in the last one.
State =
- MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, AF_IfNameIndex);
+ MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex);
C.addTransition(State);
}
-void MallocChecker::checkIfFreeNameIndex(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
bool IsKnownToBeAllocatedMemory = false;
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
AF_IfNameIndex);
C.addTransition(State);
}
-void MallocChecker::checkCXXNewOrCXXDelete(CheckerContext &C,
- const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
bool IsKnownToBeAllocatedMemory = false;
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ assert(isStandardNewDelete(Call));
- const FunctionDecl *FD = C.getCalleeDecl(CE);
// 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.
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
switch (FD->getOverloadedOperator()) {
case OO_New:
State =
- MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNew);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
+ MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew);
+ State = ProcessZeroAllocCheck(Call, 0, State);
break;
case OO_Array_New:
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
AF_CXXNewArray);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(Call, 0, State);
break;
case OO_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
AF_CXXNew);
break;
case OO_Array_Delete:
- State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory,
+ State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
AF_CXXNewArray);
break;
default:
C.addTransition(State);
}
-void MallocChecker::checkGMalloc0(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkGMalloc0(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
SValBuilder &svalBuilder = C.getSValBuilder();
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
C.addTransition(State);
}
-void MallocChecker::checkGMemdup(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
- State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
+void MallocChecker::checkGMemdup(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = MallocMemAux(C, Call, Call.getArgExpr(1), UndefinedVal(), State,
+ AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
-void MallocChecker::checkGMallocN(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkGMallocN(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
SVal Init = UndefinedVal();
- SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
- State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
-void MallocChecker::checkGMallocN0(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkGMallocN0(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
SValBuilder &SB = C.getSValBuilder();
SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
- SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
- State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc);
- State = ProcessZeroAllocCheck(C, CE, 0, State);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc);
+ State = ProcessZeroAllocCheck(Call, 0, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
C.addTransition(State);
}
-void MallocChecker::checkReallocN(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
- State = ReallocMemAux(C, CE, /*ShouldFreeOnFail=*/false, State, AF_Malloc,
+void MallocChecker::checkReallocN(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, AF_Malloc,
/*SuffixWithN=*/true);
- State = ProcessZeroAllocCheck(C, CE, 1, State);
- State = ProcessZeroAllocCheck(C, CE, 2, State);
+ State = ProcessZeroAllocCheck(Call, 1, State);
+ State = ProcessZeroAllocCheck(Call, 2, State);
C.addTransition(State);
}
-void MallocChecker::checkOwnershipAttr(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef State) const {
+void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (ShouldIncludeOwnershipAnnotatedFunctions ||
ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
switch (I->getOwnKind()) {
case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, I, State);
+ State = MallocMemReturnsAttr(C, Call, I, State);
break;
case OwnershipAttr::Takes:
case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, I, State);
+ State = FreeMemAttr(C, Call, I, State);
break;
}
}
CheckerContext &C) const {
if (C.wasInlined)
return;
-
- const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return;
-
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
+ if (!Call.getOriginExpr())
return;
ProgramStateRef State = C.getState();
if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) {
- (*Callback)(this, C, CE, State);
+ (*Callback)(this, Call, C);
return;
}
if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) {
- (*Callback)(this, C, CE, State);
+ (*Callback)(this, Call, C);
return;
}
if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) {
- (*Callback)(this, C, CE, State);
+ (*Callback)(this, Call, C);
return;
}
if (isStandardNewDelete(Call)) {
- checkCXXNewOrCXXDelete(C, CE, State);
+ checkCXXNewOrCXXDelete(Call, C);
return;
}
- checkOwnershipAttr(C, CE, State);
+ checkOwnershipAttr(Call, C);
}
// Performs a 0-sized allocations check.
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
- CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg,
- ProgramStateRef State, Optional<SVal> RetVal) {
+ const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
if (!RetVal)
- RetVal = C.getSVal(E);
+ RetVal = Call.getReturnValue();
const Expr *Arg = nullptr;
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(Call.getOriginExpr())) {
Arg = CE->getArg(IndexOfSizeArg);
- }
- else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
- if (NE->isArray())
+ } else if (const CXXNewExpr *NE =
+ dyn_cast<CXXNewExpr>(Call.getOriginExpr())) {
+ if (NE->isArray()) {
Arg = *NE->getArraySize();
- else
+ } else {
return State;
- }
- else
+ }
+ } else
llvm_unreachable("not a CallExpr or CXXNewExpr");
assert(Arg);
- Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>();
+ auto DefArgVal =
+ State->getSVal(Arg, Call.getLocationContext()).getAs<DefinedSVal>();
if (!DefArgVal)
return State;
// Check if the allocation size is 0.
ProgramStateRef TrueState, FalseState;
- SValBuilder &SvalBuilder = C.getSValBuilder();
+ SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
DefinedSVal Zero =
SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
return false;
}
-void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
- CheckerContext &C, SVal Target,
- AllocationFamily Family) const {
- if (!isStandardNewDelete(NE->getOperatorNew()))
- return;
+ProgramStateRef
+MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
+ CheckerContext &C,
+ AllocationFamily Family) const {
+ if (!isStandardNewDelete(Call))
+ return nullptr;
+ const CXXNewExpr *NE = Call.getOriginExpr();
const ParentMap &PM = C.getLocationContext()->getParentMap();
+ ProgramStateRef State = C.getState();
// Non-trivial constructors have a chance to escape 'this', but marking all
// invocations of trivial constructors as escaped would cause too great of
// reduction of true positives, so let's just do that for constructors that
// have an argument of a pointer-to-record type.
if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE))
- return;
+ return State;
- ProgramStateRef State = C.getState();
// The return value from operator new is bound to a specified initialization
// value (if any) and we don't want to loose this value. So we call
// MallocUpdateRefState() instead of MallocMemAux() which breaks the
// existing binding.
+ SVal Target = Call.getObjectUnderConstruction();
State = MallocUpdateRefState(C, NE, State, Family, Target);
State = addExtentSize(C, NE, State, Target);
- State = ProcessZeroAllocCheck(C, NE, 0, State, Target);
- C.addTransition(State);
-}
-
-void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
- if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator) {
- if (NE->isArray())
- processNewAllocation(NE, C, C.getSVal(NE),
- (NE->isArray() ? AF_CXXNewArray : AF_CXXNew));
- }
+ State = ProcessZeroAllocCheck(Call, 0, State, Target);
+ return State;
}
void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call,
CheckerContext &C) const {
if (!C.wasInlined) {
- processNewAllocation(
- Call.getOriginExpr(), C, Call.getObjectUnderConstruction(),
+ ProgramStateRef State = processNewAllocation(
+ Call, C,
(Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew));
+ C.addTransition(State);
}
}
bool IsKnownToBeAllocatedMemory;
ProgramStateRef State =
- FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),
+ FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),
/*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc,
/*RetNullOnFailure=*/true);
}
ProgramStateRef
-MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
const OwnershipAttr *Att,
ProgramStateRef State) const {
if (!State)
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
- State, AF_Malloc);
+ return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()),
+ UndefinedVal(), State, AF_Malloc);
}
- return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State, AF_Malloc);
+ return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
- const CallExpr *CE,
+ const CallEvent &Call,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family) {
if (!State)
return nullptr;
- return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family);
+ assert(SizeEx);
+ return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
- const CallExpr *CE, SVal Size,
+ const CallEvent &Call, SVal Size,
SVal Init, ProgramStateRef State,
AllocationFamily Family) {
if (!State)
return nullptr;
+ const Expr *CE = Call.getOriginExpr();
+
// We expect the malloc functions to return a pointer.
if (!Loc::isLocType(CE->getType()))
return nullptr;
}
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
- const CallExpr *CE,
+ const CallEvent &Call,
const OwnershipAttr *Att,
ProgramStateRef State) const {
if (!State)
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI =
- FreeMemAux(C, CE, State, Arg.getASTIndex(),
+ FreeMemAux(C, Call, State, Arg.getASTIndex(),
Att->getOwnKind() == OwnershipAttr::Holds,
IsKnownToBeAllocated, AF_Malloc);
if (StateI)
return State;
}
-ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
+ const CallEvent &Call,
ProgramStateRef State, unsigned Num,
bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family,
if (!State)
return nullptr;
- if (CE->getNumArgs() < (Num + 1))
+ if (Call.getNumArgs() < (Num + 1))
return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated,
- Family, ReturnsNullOnFailure);
+ return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold,
+ IsKnownToBeAllocated, Family, ReturnsNullOnFailure);
}
/// Checks if the previous call to free on the given symbol failed - if free
}
ProgramStateRef MallocChecker::FreeMemAux(
- CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr,
+ CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
AllocationFamily Family, bool ReturnsNullOnFailure) const {
return nullptr;
const MemRegion *R = ArgVal.getAsRegion();
+ const Expr *ParentExpr = Call.getOriginExpr();
// Nonlocs can't be freed, of course.
// Non-region locations (labels and fixed addresses) also shouldn't be freed.
}
ProgramStateRef
-MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE,
+MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
bool ShouldFreeOnFail, ProgramStateRef State,
AllocationFamily Family, bool SuffixWithN) const {
if (!State)
return nullptr;
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
+
if (SuffixWithN && CE->getNumArgs() < 3)
return nullptr;
else if (CE->getNumArgs() < 2)
// If the ptr is NULL and the size is not 0, the call is equivalent to
// malloc(size).
if (PrtIsNull && !SizeIsZero) {
- ProgramStateRef stateMalloc =
- MallocMemAux(C, CE, TotalSize, UndefinedVal(), StatePtrIsNull, Family);
+ ProgramStateRef stateMalloc = MallocMemAux(
+ C, Call, TotalSize, UndefinedVal(), StatePtrIsNull, Family);
return stateMalloc;
}
// If size was equal to 0, either NULL or a pointer suitable to be passed
// to free() is returned. We just free the input pointer and do not add
// any constrains on the output pointer.
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0, false,
- IsKnownToBeAllocated, Family))
+ if (ProgramStateRef stateFree = FreeMemAux(
+ C, Call, StateSizeIsZero, 0, false, IsKnownToBeAllocated, Family))
return stateFree;
// Default behavior.
if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated, Family)) {
+ FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocated, Family)) {
ProgramStateRef stateRealloc =
- MallocMemAux(C, CE, TotalSize, UnknownVal(), stateFree, Family);
+ MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Family);
if (!stateRealloc)
return nullptr;
return nullptr;
}
-ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
+ProgramStateRef MallocChecker::CallocMem(CheckerContext &C,
+ const CallEvent &Call,
ProgramStateRef State) {
if (!State)
return nullptr;
- if (CE->getNumArgs() < 2)
+ if (Call.getNumArgs() < 2)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ SVal TotalSize =
+ evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
- return MallocMemAux(C, CE, TotalSize, zeroVal, State, AF_Malloc);
+ return MallocMemAux(C, Call, TotalSize, zeroVal, State, AF_Malloc);
}
MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
ProgramStateRef State = C.getState();
bool IsKnownToBeAllocated;
- State = FreeMemAux(C, DE->getArgument(), DE, State,
+ State = FreeMemAux(C, DE->getArgument(), Call, State,
/*Hold*/ false, IsKnownToBeAllocated,
(DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew));
}
bool MallocChecker::suppressDeallocationsInSuspiciousContexts(
- const CallExpr *CE, CheckerContext &C) const {
- if (CE->getNumArgs() == 0)
+ const CallEvent &Call, CheckerContext &C) const {
+ if (Call.getNumArgs() == 0)
return false;
StringRef FunctionStr = "";
ProgramStateRef State = C.getState();
- for (const Expr *Arg : CE->arguments())
+ for (const Expr *Arg : cast<CallExpr>(Call.getOriginExpr())->arguments())
if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol())
if (const RefState *RS = State->get<RegionState>(Sym))
State = State->set<RegionState>(Sym, RefState::getEscaped(RS));