|
|
|
|
@@ -315,13 +315,24 @@ struct ReallocPair {
|
|
|
|
|
|
|
|
|
|
REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
|
|
|
|
|
|
|
|
|
|
/// Tells if the callee is one of the builtin new/delete operators, including
|
|
|
|
|
/// placement operators and other standard overloads.
|
|
|
|
|
static bool isStandardNewDelete(const FunctionDecl *FD);
|
|
|
|
|
static bool isStandardNewDelete(const CallEvent &Call) {
|
|
|
|
|
static bool isStandardNew(const FunctionDecl *FD);
|
|
|
|
|
static bool isStandardNew(const CallEvent &Call) {
|
|
|
|
|
if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))
|
|
|
|
|
return false;
|
|
|
|
|
return isStandardNewDelete(cast<FunctionDecl>(Call.getDecl()));
|
|
|
|
|
return isStandardNew(cast<FunctionDecl>(Call.getDecl()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isStandardDelete(const FunctionDecl *FD);
|
|
|
|
|
static bool isStandardDelete(const CallEvent &Call) {
|
|
|
|
|
if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))
|
|
|
|
|
return false;
|
|
|
|
|
return isStandardDelete(cast<FunctionDecl>(Call.getDecl()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Tells if the callee is one of the builtin new/delete operators, including
|
|
|
|
|
/// placement operators and other standard overloads.
|
|
|
|
|
template <typename T> static bool isStandardNewDelete(const T &FD) {
|
|
|
|
|
return isStandardDelete(FD) || isStandardNew(FD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
@@ -334,8 +345,9 @@ class MallocChecker
|
|
|
|
|
: public Checker<check::DeadSymbols, check::PointerEscape,
|
|
|
|
|
check::ConstPointerEscape, check::PreStmt<ReturnStmt>,
|
|
|
|
|
check::EndFunction, check::PreCall, check::PostCall,
|
|
|
|
|
check::NewAllocator, check::PostStmt<BlockExpr>,
|
|
|
|
|
check::PostObjCMessage, check::Location, eval::Assume> {
|
|
|
|
|
eval::Call, 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.
|
|
|
|
|
@@ -367,6 +379,7 @@ public:
|
|
|
|
|
|
|
|
|
|
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
|
|
|
|
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
|
|
|
|
|
bool evalCall(const CallEvent &Call, 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;
|
|
|
|
|
@@ -403,7 +416,8 @@ private:
|
|
|
|
|
mutable std::unique_ptr<BugType> BT_TaintedAlloc;
|
|
|
|
|
|
|
|
|
|
#define CHECK_FN(NAME) \
|
|
|
|
|
void NAME(const CallEvent &Call, CheckerContext &C) const;
|
|
|
|
|
void NAME(ProgramStateRef State, const CallEvent &Call, CheckerContext &C) \
|
|
|
|
|
const;
|
|
|
|
|
|
|
|
|
|
CHECK_FN(checkFree)
|
|
|
|
|
CHECK_FN(checkIfNameIndex)
|
|
|
|
|
@@ -423,11 +437,12 @@ private:
|
|
|
|
|
CHECK_FN(checkReallocN)
|
|
|
|
|
CHECK_FN(checkOwnershipAttr)
|
|
|
|
|
|
|
|
|
|
void checkRealloc(const CallEvent &Call, CheckerContext &C,
|
|
|
|
|
bool ShouldFreeOnFail) const;
|
|
|
|
|
void checkRealloc(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C, bool ShouldFreeOnFail) const;
|
|
|
|
|
|
|
|
|
|
using CheckFn = std::function<void(const MallocChecker *,
|
|
|
|
|
const CallEvent &Call, CheckerContext &C)>;
|
|
|
|
|
using CheckFn =
|
|
|
|
|
std::function<void(const MallocChecker *, ProgramStateRef State,
|
|
|
|
|
const CallEvent &Call, CheckerContext &C)>;
|
|
|
|
|
|
|
|
|
|
const CallDescriptionMap<CheckFn> PreFnMap{
|
|
|
|
|
// NOTE: the following CallDescription also matches the C++ standard
|
|
|
|
|
@@ -436,6 +451,13 @@ private:
|
|
|
|
|
{{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::preGetdelim},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CallDescriptionMap<CheckFn> PostFnMap{
|
|
|
|
|
// NOTE: the following CallDescription also matches the C++ standard
|
|
|
|
|
// library function std::getline(); the callback will filter it out.
|
|
|
|
|
{{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::checkGetdelim},
|
|
|
|
|
{{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::checkGetdelim},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CallDescriptionMap<CheckFn> FreeingMemFnMap{
|
|
|
|
|
{{CDM::CLibrary, {"free"}, 1}, &MallocChecker::checkFree},
|
|
|
|
|
{{CDM::CLibrary, {"if_freenameindex"}, 1},
|
|
|
|
|
@@ -446,10 +468,13 @@ private:
|
|
|
|
|
|
|
|
|
|
bool isFreeingCall(const CallEvent &Call) const;
|
|
|
|
|
static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func);
|
|
|
|
|
static bool isFreeingOwnershipAttrCall(const CallEvent &Call);
|
|
|
|
|
static bool isAllocatingOwnershipAttrCall(const FunctionDecl *Func);
|
|
|
|
|
static bool isAllocatingOwnershipAttrCall(const CallEvent &Call);
|
|
|
|
|
|
|
|
|
|
friend class NoMemOwnershipChangeVisitor;
|
|
|
|
|
|
|
|
|
|
CallDescriptionMap<CheckFn> AllocatingMemFnMap{
|
|
|
|
|
CallDescriptionMap<CheckFn> AllocaMemFnMap{
|
|
|
|
|
{{CDM::CLibrary, {"alloca"}, 1}, &MallocChecker::checkAlloca},
|
|
|
|
|
{{CDM::CLibrary, {"_alloca"}, 1}, &MallocChecker::checkAlloca},
|
|
|
|
|
// The line for "alloca" also covers "__builtin_alloca", but the
|
|
|
|
|
@@ -457,6 +482,9 @@ private:
|
|
|
|
|
// extra argument:
|
|
|
|
|
{{CDM::CLibrary, {"__builtin_alloca_with_align"}, 2},
|
|
|
|
|
&MallocChecker::checkAlloca},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CallDescriptionMap<CheckFn> AllocatingMemFnMap{
|
|
|
|
|
{{CDM::CLibrary, {"malloc"}, 1}, &MallocChecker::checkBasicAlloc},
|
|
|
|
|
{{CDM::CLibrary, {"malloc"}, 3}, &MallocChecker::checkKernelMalloc},
|
|
|
|
|
{{CDM::CLibrary, {"calloc"}, 2}, &MallocChecker::checkCalloc},
|
|
|
|
|
@@ -481,23 +509,20 @@ private:
|
|
|
|
|
|
|
|
|
|
CallDescriptionMap<CheckFn> ReallocatingMemFnMap{
|
|
|
|
|
{{CDM::CLibrary, {"realloc"}, 2},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
|
|
|
|
|
{{CDM::CLibrary, {"reallocf"}, 2},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, true)},
|
|
|
|
|
{{CDM::CLibrary, {"g_realloc"}, 2},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
|
|
|
|
|
{{CDM::CLibrary, {"g_try_realloc"}, 2},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
|
|
|
|
|
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},
|
|
|
|
|
{{CDM::CLibrary, {"g_realloc_n"}, 3}, &MallocChecker::checkReallocN},
|
|
|
|
|
{{CDM::CLibrary, {"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN},
|
|
|
|
|
|
|
|
|
|
// NOTE: the following CallDescription also matches the C++ standard
|
|
|
|
|
// library function std::getline(); the callback will filter it out.
|
|
|
|
|
{{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::checkGetdelim},
|
|
|
|
|
{{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::checkGetdelim},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool isMemCall(const CallEvent &Call) const;
|
|
|
|
|
bool hasOwnershipReturns(const CallEvent &Call) const;
|
|
|
|
|
bool hasOwnershipTakesHolds(const CallEvent &Call) const;
|
|
|
|
|
void reportTaintBug(StringRef Msg, ProgramStateRef State, CheckerContext &C,
|
|
|
|
|
llvm::ArrayRef<SymbolRef> TaintedSyms,
|
|
|
|
|
AllocationFamily Family) const;
|
|
|
|
|
@@ -531,8 +556,8 @@ private:
|
|
|
|
|
/// \param [in] RetVal Specifies the newly allocated pointer value;
|
|
|
|
|
/// if unspecified, the value of expression \p E is used.
|
|
|
|
|
[[nodiscard]] static ProgramStateRef
|
|
|
|
|
ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg,
|
|
|
|
|
ProgramStateRef State,
|
|
|
|
|
ProcessZeroAllocCheck(CheckerContext &C, const CallEvent &Call,
|
|
|
|
|
const unsigned IndexOfSizeArg, ProgramStateRef State,
|
|
|
|
|
std::optional<SVal> RetVal = std::nullopt);
|
|
|
|
|
|
|
|
|
|
/// Model functions with the ownership_returns attribute.
|
|
|
|
|
@@ -554,6 +579,17 @@ private:
|
|
|
|
|
[[nodiscard]] ProgramStateRef
|
|
|
|
|
MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
|
|
|
|
|
const OwnershipAttr *Att, ProgramStateRef State) const;
|
|
|
|
|
/// Models memory allocation.
|
|
|
|
|
///
|
|
|
|
|
/// \param [in] C Checker context.
|
|
|
|
|
/// \param [in] Call The expression that allocates memory.
|
|
|
|
|
/// \param [in] State The \c ProgramState right before allocation.
|
|
|
|
|
/// \param [in] isAlloca Is the allocation function alloca-like
|
|
|
|
|
/// \returns The ProgramState with returnValue bound
|
|
|
|
|
[[nodiscard]] ProgramStateRef MallocBindRetVal(CheckerContext &C,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
ProgramStateRef State,
|
|
|
|
|
bool isAlloca) const;
|
|
|
|
|
|
|
|
|
|
/// Models memory allocation.
|
|
|
|
|
///
|
|
|
|
|
@@ -1031,13 +1067,28 @@ public:
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
|
|
static bool isStandardNewDelete(const FunctionDecl *FD) {
|
|
|
|
|
static bool isStandardNew(const FunctionDecl *FD) {
|
|
|
|
|
if (!FD)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
|
|
|
|
|
if (Kind != OO_New && Kind != OO_Array_New && Kind != OO_Delete &&
|
|
|
|
|
Kind != OO_Array_Delete)
|
|
|
|
|
if (Kind != OO_New && Kind != OO_Array_New)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// This is standard if and only if it's not defined in a user file.
|
|
|
|
|
SourceLocation L = FD->getLocation();
|
|
|
|
|
// If the header for operator delete is not included, it's still defined
|
|
|
|
|
// in an invalid source location. Check to make sure we don't crash.
|
|
|
|
|
return !L.isValid() ||
|
|
|
|
|
FD->getASTContext().getSourceManager().isInSystemHeader(L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isStandardDelete(const FunctionDecl *FD) {
|
|
|
|
|
if (!FD)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
|
|
|
|
|
if (Kind != OO_Delete && Kind != OO_Array_Delete)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// This is standard if and only if it's not defined in a user file.
|
|
|
|
|
@@ -1052,6 +1103,12 @@ static bool isStandardNewDelete(const FunctionDecl *FD) {
|
|
|
|
|
// Methods of MallocChecker and MallocBugVisitor.
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
bool MallocChecker::isFreeingOwnershipAttrCall(const CallEvent &Call) {
|
|
|
|
|
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
|
|
|
|
|
|
|
|
|
|
return Func && isFreeingOwnershipAttrCall(Func);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MallocChecker::isFreeingOwnershipAttrCall(const FunctionDecl *Func) {
|
|
|
|
|
if (Func->hasAttrs()) {
|
|
|
|
|
for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
|
|
|
|
|
@@ -1067,15 +1124,27 @@ bool MallocChecker::isFreeingCall(const CallEvent &Call) const {
|
|
|
|
|
if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
|
|
|
|
|
return isFreeingOwnershipAttrCall(Func);
|
|
|
|
|
return isFreeingOwnershipAttrCall(Call);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MallocChecker::isAllocatingOwnershipAttrCall(const CallEvent &Call) {
|
|
|
|
|
const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
|
|
|
|
|
|
|
|
|
|
return Func && isAllocatingOwnershipAttrCall(Func);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MallocChecker::isAllocatingOwnershipAttrCall(const FunctionDecl *Func) {
|
|
|
|
|
for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {
|
|
|
|
|
if (I->getOwnKind() == OwnershipAttr::Returns)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MallocChecker::isMemCall(const CallEvent &Call) const {
|
|
|
|
|
if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) ||
|
|
|
|
|
ReallocatingMemFnMap.lookup(Call))
|
|
|
|
|
AllocaMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (!ShouldIncludeOwnershipAnnotatedFunctions)
|
|
|
|
|
@@ -1182,18 +1251,18 @@ SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
|
|
|
|
|
return TotalSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkBasicAlloc(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkBasicAlloc(ProgramStateRef State,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkKernelMalloc(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkKernelMalloc(ProgramStateRef State,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
std::optional<ProgramStateRef> MaybeState =
|
|
|
|
|
performKernelMalloc(Call, C, State);
|
|
|
|
|
if (MaybeState)
|
|
|
|
|
@@ -1226,7 +1295,8 @@ static bool isGRealloc(const CallEvent &Call) {
|
|
|
|
|
AC.UnsignedLongTy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkRealloc(const CallEvent &Call, CheckerContext &C,
|
|
|
|
|
void MallocChecker::checkRealloc(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C,
|
|
|
|
|
bool ShouldFreeOnFail) const {
|
|
|
|
|
// Ignore calls to functions whose type does not match the expected type of
|
|
|
|
|
// either the standard realloc or g_realloc from GLib.
|
|
|
|
|
@@ -1236,24 +1306,22 @@ void MallocChecker::checkRealloc(const CallEvent &Call, CheckerContext &C,
|
|
|
|
|
if (!isStandardRealloc(Call) && !isGRealloc(Call))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
State = ReallocMemAux(C, Call, ShouldFreeOnFail, State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkCalloc(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkCalloc(ProgramStateRef State, 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);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
void MallocChecker::checkFree(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
bool IsKnownToBeAllocatedMemory = false;
|
|
|
|
|
if (suppressDeallocationsInSuspiciousContexts(Call, C))
|
|
|
|
|
return;
|
|
|
|
|
@@ -1262,29 +1330,28 @@ void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const {
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkAlloca(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkAlloca(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
|
|
|
|
|
AllocationFamily(AF_Alloca));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkStrdup(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkStrdup(ProgramStateRef State, 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, AllocationFamily(AF_Malloc));
|
|
|
|
|
State = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkIfNameIndex(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkIfNameIndex(ProgramStateRef State,
|
|
|
|
|
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, Call, UnknownVal(), UnknownVal(), State,
|
|
|
|
|
@@ -1293,18 +1360,18 @@ void MallocChecker::checkIfNameIndex(const CallEvent &Call,
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkIfFreeNameIndex(ProgramStateRef State,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
bool IsKnownToBeAllocatedMemory = false;
|
|
|
|
|
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
|
|
|
|
|
AllocationFamily(AF_IfNameIndex));
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,
|
|
|
|
|
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)
|
|
|
|
|
@@ -1321,12 +1388,12 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
|
|
|
|
|
case OO_New:
|
|
|
|
|
State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
|
|
|
|
|
AllocationFamily(AF_CXXNew));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
break;
|
|
|
|
|
case OO_Array_New:
|
|
|
|
|
State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,
|
|
|
|
|
AllocationFamily(AF_CXXNewArray));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
break;
|
|
|
|
|
case OO_Delete:
|
|
|
|
|
State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,
|
|
|
|
|
@@ -1344,48 +1411,44 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call,
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkGMalloc0(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkGMalloc0(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
SValBuilder &svalBuilder = C.getSValBuilder();
|
|
|
|
|
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
|
|
|
|
|
State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkGMemdup(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkGMemdup(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
State = MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkGMallocN(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkGMallocN(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
SVal Init = UndefinedVal();
|
|
|
|
|
SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
|
|
|
|
|
State = MallocMemAux(C, Call, TotalSize, Init, State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkGMallocN0(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkGMallocN0(ProgramStateRef State, 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, Call.getArgExpr(0), Call.getArgExpr(1));
|
|
|
|
|
State = MallocMemAux(C, Call, TotalSize, Init, State,
|
|
|
|
|
AllocationFamily(AF_Malloc));
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1395,14 +1458,13 @@ static bool isFromStdNamespace(const CallEvent &Call) {
|
|
|
|
|
return FD->isInStdNamespace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::preGetdelim(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::preGetdelim(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
// Discard calls to the C++ standard library function std::getline(), which
|
|
|
|
|
// is completely unrelated to the POSIX getline() that we're checking.
|
|
|
|
|
if (isFromStdNamespace(Call))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
const auto LinePtr = getPointeeVal(Call.getArgSVal(0), State);
|
|
|
|
|
if (!LinePtr)
|
|
|
|
|
return;
|
|
|
|
|
@@ -1419,22 +1481,19 @@ void MallocChecker::preGetdelim(const CallEvent &Call,
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkGetdelim(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkGetdelim(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
// Discard calls to the C++ standard library function std::getline(), which
|
|
|
|
|
// is completely unrelated to the POSIX getline() that we're checking.
|
|
|
|
|
if (isFromStdNamespace(Call))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
// Handle the post-conditions of getline and getdelim:
|
|
|
|
|
// Register the new conjured value as an allocated buffer.
|
|
|
|
|
const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
|
|
|
|
|
if (!CE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SValBuilder &SVB = C.getSValBuilder();
|
|
|
|
|
|
|
|
|
|
const auto LinePtr =
|
|
|
|
|
getPointeeVal(Call.getArgSVal(0), State)->getAs<DefinedSVal>();
|
|
|
|
|
const auto Size =
|
|
|
|
|
@@ -1442,25 +1501,24 @@ void MallocChecker::checkGetdelim(const CallEvent &Call,
|
|
|
|
|
if (!LinePtr || !Size || !LinePtr->getAsRegion())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
State = setDynamicExtent(State, LinePtr->getAsRegion(), *Size, SVB);
|
|
|
|
|
State = setDynamicExtent(State, LinePtr->getAsRegion(), *Size);
|
|
|
|
|
C.addTransition(MallocUpdateRefState(C, CE, State,
|
|
|
|
|
AllocationFamily(AF_Malloc), *LinePtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkReallocN(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkReallocN(ProgramStateRef State, const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State,
|
|
|
|
|
AllocationFamily(AF_Malloc),
|
|
|
|
|
/*SuffixWithN=*/true);
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 2, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 1, State);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 2, State);
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
|
|
|
|
|
void MallocChecker::checkOwnershipAttr(ProgramStateRef State,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
|
|
|
|
|
if (!CE)
|
|
|
|
|
return;
|
|
|
|
|
@@ -1487,48 +1545,67 @@ void MallocChecker::checkOwnershipAttr(const CallEvent &Call,
|
|
|
|
|
C.addTransition(State);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkPostCall(const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
if (C.wasInlined)
|
|
|
|
|
return;
|
|
|
|
|
bool MallocChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
|
|
|
|
|
if (!Call.getOriginExpr())
|
|
|
|
|
return;
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
|
|
|
|
|
if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) {
|
|
|
|
|
(*Callback)(this, Call, C);
|
|
|
|
|
return;
|
|
|
|
|
(*Callback)(this, State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) {
|
|
|
|
|
(*Callback)(this, Call, C);
|
|
|
|
|
return;
|
|
|
|
|
State = MallocBindRetVal(C, Call, State, false);
|
|
|
|
|
(*Callback)(this, State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) {
|
|
|
|
|
(*Callback)(this, Call, C);
|
|
|
|
|
return;
|
|
|
|
|
State = MallocBindRetVal(C, Call, State, false);
|
|
|
|
|
(*Callback)(this, State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isStandardNewDelete(Call)) {
|
|
|
|
|
checkCXXNewOrCXXDelete(Call, C);
|
|
|
|
|
return;
|
|
|
|
|
if (isStandardNew(Call)) {
|
|
|
|
|
State = MallocBindRetVal(C, Call, State, false);
|
|
|
|
|
checkCXXNewOrCXXDelete(State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkOwnershipAttr(Call, C);
|
|
|
|
|
if (isStandardDelete(Call)) {
|
|
|
|
|
checkCXXNewOrCXXDelete(State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const CheckFn *Callback = AllocaMemFnMap.lookup(Call)) {
|
|
|
|
|
State = MallocBindRetVal(C, Call, State, true);
|
|
|
|
|
(*Callback)(this, State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isFreeingOwnershipAttrCall(Call)) {
|
|
|
|
|
checkOwnershipAttr(State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isAllocatingOwnershipAttrCall(Call)) {
|
|
|
|
|
State = MallocBindRetVal(C, Call, State, false);
|
|
|
|
|
checkOwnershipAttr(State, Call, C);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Performs a 0-sized allocations check.
|
|
|
|
|
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
|
|
|
|
|
const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State,
|
|
|
|
|
std::optional<SVal> RetVal) {
|
|
|
|
|
CheckerContext &C, const CallEvent &Call, const unsigned IndexOfSizeArg,
|
|
|
|
|
ProgramStateRef State, std::optional<SVal> RetVal) {
|
|
|
|
|
if (!State)
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
if (!RetVal)
|
|
|
|
|
RetVal = Call.getReturnValue();
|
|
|
|
|
|
|
|
|
|
const Expr *Arg = nullptr;
|
|
|
|
|
|
|
|
|
|
if (const CallExpr *CE = dyn_cast<CallExpr>(Call.getOriginExpr())) {
|
|
|
|
|
@@ -1545,6 +1622,9 @@ ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!RetVal)
|
|
|
|
|
RetVal = State->getSVal(Call.getOriginExpr(), C.getLocationContext());
|
|
|
|
|
|
|
|
|
|
assert(Arg);
|
|
|
|
|
|
|
|
|
|
auto DefArgVal =
|
|
|
|
|
@@ -1656,7 +1736,7 @@ MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
State = MallocUpdateRefState(C, NE, State, Family, Target);
|
|
|
|
|
State = ProcessZeroAllocCheck(Call, 0, State, Target);
|
|
|
|
|
State = ProcessZeroAllocCheck(C, Call, 0, State, Target);
|
|
|
|
|
return State;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1736,6 +1816,24 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
|
|
|
|
|
return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, Family);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProgramStateRef MallocChecker::MallocBindRetVal(CheckerContext &C,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
ProgramStateRef State,
|
|
|
|
|
bool isAlloca) const {
|
|
|
|
|
const Expr *CE = Call.getOriginExpr();
|
|
|
|
|
|
|
|
|
|
// We expect the allocation functions to return a pointer.
|
|
|
|
|
if (!Loc::isLocType(CE->getType()))
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
unsigned Count = C.blockCount();
|
|
|
|
|
SValBuilder &SVB = C.getSValBuilder();
|
|
|
|
|
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
|
|
|
|
|
DefinedSVal RetVal = isAlloca ? SVB.getAllocaRegionVal(CE, LCtx, Count)
|
|
|
|
|
: SVB.getConjuredHeapSymbolVal(CE, LCtx, Count);
|
|
|
|
|
return State->BindExpr(CE, C.getLocationContext(), RetVal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
|
|
|
|
|
const CallEvent &Call,
|
|
|
|
|
const Expr *SizeEx, SVal Init,
|
|
|
|
|
@@ -1814,20 +1912,12 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
|
|
|
|
|
const Expr *CE = Call.getOriginExpr();
|
|
|
|
|
|
|
|
|
|
// We expect the malloc functions to return a pointer.
|
|
|
|
|
if (!Loc::isLocType(CE->getType()))
|
|
|
|
|
return nullptr;
|
|
|
|
|
// Should have been already checked.
|
|
|
|
|
assert(Loc::isLocType(CE->getType()) &&
|
|
|
|
|
"Allocation functions must return a pointer");
|
|
|
|
|
|
|
|
|
|
// Bind the return value to the symbolic value from the heap region.
|
|
|
|
|
// TODO: move use of this functions to an EvalCall callback, becasue
|
|
|
|
|
// BindExpr() should'nt be used elsewhere.
|
|
|
|
|
unsigned Count = C.blockCount();
|
|
|
|
|
SValBuilder &SVB = C.getSValBuilder();
|
|
|
|
|
const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
|
|
|
|
|
DefinedSVal RetVal = ((Family.Kind == AF_Alloca)
|
|
|
|
|
? SVB.getAllocaRegionVal(CE, LCtx, Count)
|
|
|
|
|
: SVB.getConjuredHeapSymbolVal(CE, LCtx, Count)
|
|
|
|
|
.castAs<DefinedSVal>());
|
|
|
|
|
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
|
|
|
|
|
SVal RetVal = State->getSVal(CE, C.getLocationContext());
|
|
|
|
|
|
|
|
|
|
// Fill the region with the initialization value.
|
|
|
|
|
State = State->bindDefaultInitial(RetVal, Init, LCtx);
|
|
|
|
|
@@ -1840,7 +1930,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
|
|
|
|
|
|
|
|
|
|
// Set the region's extent.
|
|
|
|
|
State = setDynamicExtent(State, RetVal.getAsRegion(),
|
|
|
|
|
Size.castAs<DefinedOrUnknownSVal>(), SVB);
|
|
|
|
|
Size.castAs<DefinedOrUnknownSVal>());
|
|
|
|
|
|
|
|
|
|
return MallocUpdateRefState(C, CE, State, Family);
|
|
|
|
|
}
|
|
|
|
|
@@ -1854,7 +1944,7 @@ static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
|
|
|
|
|
|
|
|
|
|
// Get the return value.
|
|
|
|
|
if (!RetVal)
|
|
|
|
|
RetVal = C.getSVal(E);
|
|
|
|
|
RetVal = State->getSVal(E, C.getLocationContext());
|
|
|
|
|
|
|
|
|
|
// We expect the malloc functions to return a pointer.
|
|
|
|
|
if (!RetVal->getAs<Loc>())
|
|
|
|
|
@@ -1862,20 +1952,15 @@ static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
|
|
|
|
|
|
|
|
|
|
SymbolRef Sym = RetVal->getAsLocSymbol();
|
|
|
|
|
|
|
|
|
|
// This is a return value of a function that was not inlined, such as malloc()
|
|
|
|
|
// or new(). We've checked that in the caller. Therefore, it must be a symbol.
|
|
|
|
|
assert(Sym);
|
|
|
|
|
// FIXME: In theory this assertion should fail for `alloca()` calls (because
|
|
|
|
|
// `AllocaRegion`s are not symbolic); but in practice this does not happen.
|
|
|
|
|
// As the current code appears to work correctly, I'm not touching this issue
|
|
|
|
|
// now, but it would be good to investigate and clarify this.
|
|
|
|
|
// Also note that perhaps the special `AllocaRegion` should be replaced by
|
|
|
|
|
// `SymbolicRegion` (or turned into a subclass of `SymbolicRegion`) to enable
|
|
|
|
|
// proper tracking of memory allocated by `alloca()` -- and after that change
|
|
|
|
|
// this assertion would become valid again.
|
|
|
|
|
// NOTE: If this was an `alloca()` call, then `RetVal` holds an
|
|
|
|
|
// `AllocaRegion`, so `Sym` will be a nullpointer because `AllocaRegion`s do
|
|
|
|
|
// not have an associated symbol. However, this distinct region type means
|
|
|
|
|
// that we don't need to store anything about them in `RegionState`.
|
|
|
|
|
|
|
|
|
|
// Set the symbol's state to Allocated.
|
|
|
|
|
return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));
|
|
|
|
|
if (Sym)
|
|
|
|
|
return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));
|
|
|
|
|
|
|
|
|
|
return State;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
|
|
|
|
|
@@ -2214,6 +2299,14 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
|
|
|
|
|
// that.
|
|
|
|
|
assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family));
|
|
|
|
|
|
|
|
|
|
// Assume that after memory is freed, it contains unknown values. This
|
|
|
|
|
// conforts languages standards, since reading from freed memory is considered
|
|
|
|
|
// UB and may result in arbitrary value.
|
|
|
|
|
State = State->invalidateRegions({location}, Call.getOriginExpr(),
|
|
|
|
|
C.blockCount(), C.getLocationContext(),
|
|
|
|
|
/*CausesPointerEscape=*/false,
|
|
|
|
|
/*InvalidatedSymbols=*/nullptr);
|
|
|
|
|
|
|
|
|
|
// Normal free.
|
|
|
|
|
if (Hold)
|
|
|
|
|
return State->set<RegionState>(SymBase,
|
|
|
|
|
@@ -2781,6 +2874,7 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
|
|
|
|
|
return stateMalloc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Proccess as allocation of 0 bytes.
|
|
|
|
|
if (PrtIsNull && SizeIsZero)
|
|
|
|
|
return State;
|
|
|
|
|
|
|
|
|
|
@@ -2815,7 +2909,7 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
|
|
|
|
|
|
|
|
|
|
// Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
|
|
|
|
|
SymbolRef FromPtr = arg0Val.getLocSymbolInBase();
|
|
|
|
|
SVal RetVal = C.getSVal(CE);
|
|
|
|
|
SVal RetVal = stateRealloc->getSVal(CE, C.getLocationContext());
|
|
|
|
|
SymbolRef ToPtr = RetVal.getAsSymbol();
|
|
|
|
|
assert(FromPtr && ToPtr &&
|
|
|
|
|
"By this point, FreeMemAux and MallocMemAux should have checked "
|
|
|
|
|
@@ -3014,6 +3108,14 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
|
|
|
|
|
C.addTransition(state->set<RegionState>(RS), N);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkPostCall(const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
if (const auto *PostFN = PostFnMap.lookup(Call)) {
|
|
|
|
|
(*PostFN)(this, C.getState(), Call, C);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MallocChecker::checkPreCall(const CallEvent &Call,
|
|
|
|
|
CheckerContext &C) const {
|
|
|
|
|
|
|
|
|
|
@@ -3047,7 +3149,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
|
|
|
|
|
// We need to handle getline pre-conditions here before the pointed region
|
|
|
|
|
// gets invalidated by StreamChecker
|
|
|
|
|
if (const auto *PreFN = PreFnMap.lookup(Call)) {
|
|
|
|
|
(*PreFN)(this, Call, C);
|
|
|
|
|
(*PreFN)(this, C.getState(), Call, C);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|