4231 lines · cpp
1//=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines checkers that report memory management errors such as10// leak, double free, and use-after-free.11//12// The logic for modeling memory allocations is implemented in the checker13// family which is called 'MallocChecker' for historical reasons. (This name is14// inaccurate, something like 'DynamicMemory' would be more precise.)15//16// The reports produced by this backend are exposed through several frontends:17// * MallocChecker: reports all misuse of dynamic memory allocated by18// malloc, related functions (like calloc, realloc etc.) and the functions19// annotated by ownership_returns. (Here the name "MallocChecker" is20// reasonably accurate; don't confuse this checker frontend with the whole21// misnamed family.)22// * NewDeleteChecker: reports most misuse (anything but memory leaks) of23// memory managed by the C++ operators new and new[].24// * NewDeleteLeaksChecker: reports leaks of dynamic memory allocated by25// the C++ operators new and new[].26// * MismatchedDeallocatorChecker: reports situations where the allocation27// and deallocation is mismatched, e.g. memory allocated via malloc is28// passed to operator delete.29// * InnerPointerChecker: reports use of pointers to the internal buffer of30// a std::string instance after operations that invalidate them.31// * TaintedAllocChecker: reports situations where the size argument of a32// memory allocation function or array new operator is tainted (i.e. comes33// from an untrusted source and can be controlled by an attacker).34//35// In addition to these frontends this file also defines the registration36// functions for "unix.DynamicMemoryModeling". This registers the callbacks of37// the checker family MallocChecker without enabling any of the frontends and38// and handle two checker options which are attached to this "modeling39// checker" because they affect multiple checker frontends.40//41// Note that what the users see as the checker "cplusplus.InnerPointer" is a42// combination of the frontend InnerPointerChecker (within this family) which43// emits the bug reports and a separate checker class (also named44// InnerPointerChecker) which is defined in InnerPointerChecker.cpp and does a45// significant part of the modeling. This cooperation is enabled by several46// non-static helper functions that are defined within this translation unit47// and used in InnerPointerChecker.cpp.48//49//===----------------------------------------------------------------------===//50 51#include "AllocationState.h"52#include "InterCheckerAPI.h"53#include "NoOwnershipChangeVisitor.h"54#include "clang/AST/Attr.h"55#include "clang/AST/DeclCXX.h"56#include "clang/AST/DeclTemplate.h"57#include "clang/AST/Expr.h"58#include "clang/AST/ExprCXX.h"59#include "clang/AST/ParentMap.h"60#include "clang/ASTMatchers/ASTMatchFinder.h"61#include "clang/ASTMatchers/ASTMatchers.h"62#include "clang/Analysis/ProgramPoint.h"63#include "clang/Basic/LLVM.h"64#include "clang/Basic/SourceManager.h"65#include "clang/Basic/TargetInfo.h"66#include "clang/Lex/Lexer.h"67#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"68#include "clang/StaticAnalyzer/Checkers/Taint.h"69#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"70#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"71#include "clang/StaticAnalyzer/Core/Checker.h"72#include "clang/StaticAnalyzer/Core/CheckerManager.h"73#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"74#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"75#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"76#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"77#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"78#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"79#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"80#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"81#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"82#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"83#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"84#include "llvm/ADT/STLExtras.h"85#include "llvm/ADT/SmallVector.h"86#include "llvm/ADT/StringExtras.h"87#include "llvm/Support/Casting.h"88#include "llvm/Support/Compiler.h"89#include "llvm/Support/ErrorHandling.h"90#include "llvm/Support/raw_ostream.h"91#include <functional>92#include <optional>93#include <utility>94 95using namespace clang;96using namespace ento;97using namespace std::placeholders;98 99//===----------------------------------------------------------------------===//100// The types of allocation we're modeling. This is used to check whether a101// dynamically allocated object is deallocated with the correct function, like102// not using operator delete on an object created by malloc(), or alloca regions103// aren't ever deallocated manually.104//===----------------------------------------------------------------------===//105 106namespace {107 108// Used to check correspondence between allocators and deallocators.109enum AllocationFamilyKind {110 AF_None,111 AF_Malloc,112 AF_CXXNew,113 AF_CXXNewArray,114 AF_IfNameIndex,115 AF_Alloca,116 AF_InnerBuffer,117 AF_Custom,118};119 120struct AllocationFamily {121 AllocationFamilyKind Kind;122 std::optional<StringRef> CustomName;123 124 explicit AllocationFamily(AllocationFamilyKind AKind,125 std::optional<StringRef> Name = std::nullopt)126 : Kind(AKind), CustomName(Name) {127 assert((Kind != AF_Custom || CustomName.has_value()) &&128 "Custom family must specify also the name");129 130 // Preseve previous behavior when "malloc" class means AF_Malloc131 if (Kind == AF_Custom && CustomName.value() == "malloc") {132 Kind = AF_Malloc;133 CustomName = std::nullopt;134 }135 }136 137 bool operator==(const AllocationFamily &Other) const {138 return std::tie(Kind, CustomName) == std::tie(Other.Kind, Other.CustomName);139 }140 141 bool operator!=(const AllocationFamily &Other) const {142 return !(*this == Other);143 }144 145 void Profile(llvm::FoldingSetNodeID &ID) const {146 ID.AddInteger(Kind);147 148 if (Kind == AF_Custom)149 ID.AddString(CustomName.value());150 }151};152 153} // end of anonymous namespace154 155/// Print names of allocators and deallocators.156///157/// \returns true on success.158static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E);159 160/// Print expected name of an allocator based on the deallocator's family161/// derived from the DeallocExpr.162static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family);163 164/// Print expected name of a deallocator based on the allocator's165/// family.166static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family);167 168//===----------------------------------------------------------------------===//169// The state of a symbol, in terms of memory management.170//===----------------------------------------------------------------------===//171 172namespace {173 174class RefState {175 enum Kind {176 // Reference to allocated memory.177 Allocated,178 // Reference to zero-allocated memory.179 AllocatedOfSizeZero,180 // Reference to released/freed memory.181 Released,182 // The responsibility for freeing resources has transferred from183 // this reference. A relinquished symbol should not be freed.184 Relinquished,185 // We are no longer guaranteed to have observed all manipulations186 // of this pointer/memory. For example, it could have been187 // passed as a parameter to an opaque function.188 Escaped189 };190 191 const Stmt *S;192 193 Kind K;194 AllocationFamily Family;195 196 RefState(Kind k, const Stmt *s, AllocationFamily family)197 : S(s), K(k), Family(family) {198 assert(family.Kind != AF_None);199 }200 201public:202 bool isAllocated() const { return K == Allocated; }203 bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }204 bool isReleased() const { return K == Released; }205 bool isRelinquished() const { return K == Relinquished; }206 bool isEscaped() const { return K == Escaped; }207 AllocationFamily getAllocationFamily() const { return Family; }208 const Stmt *getStmt() const { return S; }209 210 bool operator==(const RefState &X) const {211 return K == X.K && S == X.S && Family == X.Family;212 }213 214 static RefState getAllocated(AllocationFamily family, const Stmt *s) {215 return RefState(Allocated, s, family);216 }217 static RefState getAllocatedOfSizeZero(const RefState *RS) {218 return RefState(AllocatedOfSizeZero, RS->getStmt(),219 RS->getAllocationFamily());220 }221 static RefState getReleased(AllocationFamily family, const Stmt *s) {222 return RefState(Released, s, family);223 }224 static RefState getRelinquished(AllocationFamily family, const Stmt *s) {225 return RefState(Relinquished, s, family);226 }227 static RefState getEscaped(const RefState *RS) {228 return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily());229 }230 231 void Profile(llvm::FoldingSetNodeID &ID) const {232 ID.AddInteger(K);233 ID.AddPointer(S);234 Family.Profile(ID);235 }236 237 LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {238 switch (K) {239#define CASE(ID) case ID: OS << #ID; break;240 CASE(Allocated)241 CASE(AllocatedOfSizeZero)242 CASE(Released)243 CASE(Relinquished)244 CASE(Escaped)245 }246 }247 248 LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }249};250 251} // end of anonymous namespace252 253REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)254 255/// Check if the memory associated with this symbol was released.256static bool isReleased(SymbolRef Sym, CheckerContext &C);257 258/// Update the RefState to reflect the new memory allocation.259/// The optional \p RetVal parameter specifies the newly allocated pointer260/// value; if unspecified, the value of expression \p E is used.261static ProgramStateRef262MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,263 AllocationFamily Family,264 std::optional<SVal> RetVal = std::nullopt);265 266//===----------------------------------------------------------------------===//267// The modeling of memory reallocation.268//269// The terminology 'toPtr' and 'fromPtr' will be used:270// toPtr = realloc(fromPtr, 20);271//===----------------------------------------------------------------------===//272 273REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)274 275namespace {276 277/// The state of 'fromPtr' after reallocation is known to have failed.278enum OwnershipAfterReallocKind {279 // The symbol needs to be freed (e.g.: realloc)280 OAR_ToBeFreedAfterFailure,281 // The symbol has been freed (e.g.: reallocf)282 OAR_FreeOnFailure,283 // The symbol doesn't have to freed (e.g.: we aren't sure if, how and where284 // 'fromPtr' was allocated:285 // void Haha(int *ptr) {286 // ptr = realloc(ptr, 67);287 // // ...288 // }289 // ).290 OAR_DoNotTrackAfterFailure291};292 293/// Stores information about the 'fromPtr' symbol after reallocation.294///295/// This is important because realloc may fail, and that needs special modeling.296/// Whether reallocation failed or not will not be known until later, so we'll297/// store whether upon failure 'fromPtr' will be freed, or needs to be freed298/// later, etc.299struct ReallocPair {300 301 // The 'fromPtr'.302 SymbolRef ReallocatedSym;303 OwnershipAfterReallocKind Kind;304 305 ReallocPair(SymbolRef S, OwnershipAfterReallocKind K)306 : ReallocatedSym(S), Kind(K) {}307 void Profile(llvm::FoldingSetNodeID &ID) const {308 ID.AddInteger(Kind);309 ID.AddPointer(ReallocatedSym);310 }311 bool operator==(const ReallocPair &X) const {312 return ReallocatedSym == X.ReallocatedSym &&313 Kind == X.Kind;314 }315};316 317} // end of anonymous namespace318 319REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)320 321static bool isStandardNew(const FunctionDecl *FD);322static bool isStandardNew(const CallEvent &Call) {323 if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))324 return false;325 return isStandardNew(cast<FunctionDecl>(Call.getDecl()));326}327 328static bool isStandardDelete(const FunctionDecl *FD);329static bool isStandardDelete(const CallEvent &Call) {330 if (!Call.getDecl() || !isa<FunctionDecl>(Call.getDecl()))331 return false;332 return isStandardDelete(cast<FunctionDecl>(Call.getDecl()));333}334 335/// Tells if the callee is one of the builtin new/delete operators, including336/// placement operators and other standard overloads.337template <typename T> static bool isStandardNewDelete(const T &FD) {338 return isStandardDelete(FD) || isStandardNew(FD);339}340 341namespace {342 343//===----------------------------------------------------------------------===//344// Utility classes that provide access to the bug types and can model that some345// of the bug types are shared by multiple checker frontends.346//===----------------------------------------------------------------------===//347 348#define BUGTYPE_PROVIDER(NAME, DEF) \349 struct NAME : virtual public CheckerFrontend { \350 BugType NAME##Bug{this, DEF, categories::MemoryError}; \351 };352 353BUGTYPE_PROVIDER(DoubleFree, "Double free")354 355struct Leak : virtual public CheckerFrontend {356 // Leaks should not be reported if they are post-dominated by a sink:357 // (1) Sinks are higher importance bugs.358 // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending359 // with __noreturn functions such as assert() or exit(). We choose not360 // to report leaks on such paths.361 BugType LeakBug{this, "Memory leak", categories::MemoryError,362 /*SuppressOnSink=*/true};363};364 365BUGTYPE_PROVIDER(UseFree, "Use-after-free")366BUGTYPE_PROVIDER(BadFree, "Bad free")367BUGTYPE_PROVIDER(FreeAlloca, "Free 'alloca()'")368BUGTYPE_PROVIDER(MismatchedDealloc, "Bad deallocator")369BUGTYPE_PROVIDER(OffsetFree, "Offset free")370BUGTYPE_PROVIDER(UseZeroAllocated, "Use of zero allocated")371 372#undef BUGTYPE_PROVIDER373 374template <typename... BT_PROVIDERS>375struct DynMemFrontend : virtual public CheckerFrontend, public BT_PROVIDERS... {376 template <typename T> const T *getAs() const {377 if constexpr (std::is_same_v<T, CheckerFrontend> ||378 (std::is_same_v<T, BT_PROVIDERS> || ...))379 return static_cast<const T *>(this);380 return nullptr;381 }382};383 384//===----------------------------------------------------------------------===//385// Definition of the MallocChecker class.386//===----------------------------------------------------------------------===//387 388class MallocChecker389 : public CheckerFamily<390 check::DeadSymbols, check::PointerEscape, check::ConstPointerEscape,391 check::PreStmt<ReturnStmt>, check::EndFunction, check::PreCall,392 check::PostCall, eval::Call, check::NewAllocator,393 check::PostStmt<BlockExpr>, check::PostObjCMessage, check::Location,394 eval::Assume> {395public:396 /// In pessimistic mode, the checker assumes that it does not know which397 /// functions might free the memory.398 /// In optimistic mode, the checker assumes that all user-defined functions399 /// which might free a pointer are annotated.400 bool ShouldIncludeOwnershipAnnotatedFunctions = false;401 402 bool ShouldRegisterNoOwnershipChangeVisitor = false;403 404 // This checker family implements many bug types and frontends, and several405 // bug types are shared between multiple frontends, so most of the frontends406 // are declared with the helper class DynMemFrontend.407 // FIXME: There is no clear reason for separating NewDelete vs NewDeleteLeaks408 // while e.g. MallocChecker covers both non-leak and leak bugs together. It409 // would be nice to redraw the boundaries between the frontends in a more410 // logical way.411 DynMemFrontend<DoubleFree, Leak, UseFree, BadFree, FreeAlloca, OffsetFree,412 UseZeroAllocated>413 MallocChecker;414 DynMemFrontend<DoubleFree, UseFree, BadFree, OffsetFree, UseZeroAllocated>415 NewDeleteChecker;416 DynMemFrontend<Leak> NewDeleteLeaksChecker;417 DynMemFrontend<FreeAlloca, MismatchedDealloc> MismatchedDeallocatorChecker;418 DynMemFrontend<UseFree> InnerPointerChecker;419 // This last frontend is associated with a single bug type which is not used420 // elsewhere and has a different bug category, so it's declared separately.421 CheckerFrontendWithBugType TaintedAllocChecker{"Tainted Memory Allocation",422 categories::TaintedData};423 424 using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>;425 426 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;427 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;428 bool evalCall(const CallEvent &Call, CheckerContext &C) const;429 430 ProgramStateRef431 handleSmartPointerConstructorArguments(const CallEvent &Call,432 ProgramStateRef State) const;433 ProgramStateRef handleSmartPointerRelatedCalls(const CallEvent &Call,434 CheckerContext &C,435 ProgramStateRef State) const;436 void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const;437 void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;438 void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;439 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;440 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;441 void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const;442 ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,443 bool Assumption) const;444 void checkLocation(SVal l, bool isLoad, const Stmt *S,445 CheckerContext &C) const;446 447 ProgramStateRef checkPointerEscape(ProgramStateRef State,448 const InvalidatedSymbols &Escaped,449 const CallEvent *Call,450 PointerEscapeKind Kind) const;451 ProgramStateRef checkConstPointerEscape(ProgramStateRef State,452 const InvalidatedSymbols &Escaped,453 const CallEvent *Call,454 PointerEscapeKind Kind) const;455 456 void printState(raw_ostream &Out, ProgramStateRef State,457 const char *NL, const char *Sep) const override;458 459 StringRef getDebugTag() const override { return "MallocChecker"; }460 461private:462#define CHECK_FN(NAME) \463 void NAME(ProgramStateRef State, const CallEvent &Call, CheckerContext &C) \464 const;465 466 CHECK_FN(checkFree)467 CHECK_FN(checkIfNameIndex)468 CHECK_FN(checkBasicAlloc)469 CHECK_FN(checkKernelMalloc)470 CHECK_FN(checkCalloc)471 CHECK_FN(checkAlloca)472 CHECK_FN(checkStrdup)473 CHECK_FN(checkIfFreeNameIndex)474 CHECK_FN(checkCXXNewOrCXXDelete)475 CHECK_FN(checkGMalloc0)476 CHECK_FN(checkGMemdup)477 CHECK_FN(checkGMallocN)478 CHECK_FN(checkGMallocN0)479 CHECK_FN(preGetDelimOrGetLine)480 CHECK_FN(checkGetDelimOrGetLine)481 CHECK_FN(checkReallocN)482 CHECK_FN(checkOwnershipAttr)483 484 void checkRealloc(ProgramStateRef State, const CallEvent &Call,485 CheckerContext &C, bool ShouldFreeOnFail) const;486 487 using CheckFn =488 std::function<void(const class MallocChecker *, ProgramStateRef State,489 const CallEvent &Call, CheckerContext &C)>;490 491 const CallDescriptionMap<CheckFn> PreFnMap{492 // NOTE: the following CallDescription also matches the C++ standard493 // library function std::getline(); the callback will filter it out.494 {{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::preGetDelimOrGetLine},495 {{CDM::CLibrary, {"getdelim"}, 4}, &MallocChecker::preGetDelimOrGetLine},496 };497 498 const CallDescriptionMap<CheckFn> PostFnMap{499 // NOTE: the following CallDescription also matches the C++ standard500 // library function std::getline(); the callback will filter it out.501 {{CDM::CLibrary, {"getline"}, 3}, &MallocChecker::checkGetDelimOrGetLine},502 {{CDM::CLibrary, {"getdelim"}, 4},503 &MallocChecker::checkGetDelimOrGetLine},504 };505 506 const CallDescriptionMap<CheckFn> FreeingMemFnMap{507 {{CDM::CLibrary, {"free"}, 1}, &MallocChecker::checkFree},508 {{CDM::CLibrary, {"if_freenameindex"}, 1},509 &MallocChecker::checkIfFreeNameIndex},510 {{CDM::CLibrary, {"kfree"}, 1}, &MallocChecker::checkFree},511 {{CDM::CLibrary, {"g_free"}, 1}, &MallocChecker::checkFree},512 };513 514 bool isFreeingCall(const CallEvent &Call) const;515 static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func);516 static bool isFreeingOwnershipAttrCall(const CallEvent &Call);517 static bool isAllocatingOwnershipAttrCall(const FunctionDecl *Func);518 static bool isAllocatingOwnershipAttrCall(const CallEvent &Call);519 520 friend class NoMemOwnershipChangeVisitor;521 522 CallDescriptionMap<CheckFn> AllocaMemFnMap{523 {{CDM::CLibrary, {"alloca"}, 1}, &MallocChecker::checkAlloca},524 {{CDM::CLibrary, {"_alloca"}, 1}, &MallocChecker::checkAlloca},525 // The line for "alloca" also covers "__builtin_alloca", but the526 // _with_align variant must be listed separately because it takes an527 // extra argument:528 {{CDM::CLibrary, {"__builtin_alloca_with_align"}, 2},529 &MallocChecker::checkAlloca},530 };531 532 CallDescriptionMap<CheckFn> AllocatingMemFnMap{533 {{CDM::CLibrary, {"malloc"}, 1}, &MallocChecker::checkBasicAlloc},534 {{CDM::CLibrary, {"malloc"}, 3}, &MallocChecker::checkKernelMalloc},535 {{CDM::CLibrary, {"calloc"}, 2}, &MallocChecker::checkCalloc},536 {{CDM::CLibrary, {"valloc"}, 1}, &MallocChecker::checkBasicAlloc},537 {{CDM::CLibrary, {"strndup"}, 2}, &MallocChecker::checkStrdup},538 {{CDM::CLibrary, {"strdup"}, 1}, &MallocChecker::checkStrdup},539 {{CDM::CLibrary, {"_strdup"}, 1}, &MallocChecker::checkStrdup},540 {{CDM::CLibrary, {"kmalloc"}, 2}, &MallocChecker::checkKernelMalloc},541 {{CDM::CLibrary, {"if_nameindex"}, 1}, &MallocChecker::checkIfNameIndex},542 {{CDM::CLibrary, {"wcsdup"}, 1}, &MallocChecker::checkStrdup},543 {{CDM::CLibrary, {"_wcsdup"}, 1}, &MallocChecker::checkStrdup},544 {{CDM::CLibrary, {"g_malloc"}, 1}, &MallocChecker::checkBasicAlloc},545 {{CDM::CLibrary, {"g_malloc0"}, 1}, &MallocChecker::checkGMalloc0},546 {{CDM::CLibrary, {"g_try_malloc"}, 1}, &MallocChecker::checkBasicAlloc},547 {{CDM::CLibrary, {"g_try_malloc0"}, 1}, &MallocChecker::checkGMalloc0},548 {{CDM::CLibrary, {"g_memdup"}, 2}, &MallocChecker::checkGMemdup},549 {{CDM::CLibrary, {"g_malloc_n"}, 2}, &MallocChecker::checkGMallocN},550 {{CDM::CLibrary, {"g_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0},551 {{CDM::CLibrary, {"g_try_malloc_n"}, 2}, &MallocChecker::checkGMallocN},552 {{CDM::CLibrary, {"g_try_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0},553 };554 555 CallDescriptionMap<CheckFn> ReallocatingMemFnMap{556 {{CDM::CLibrary, {"realloc"}, 2},557 std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},558 {{CDM::CLibrary, {"reallocf"}, 2},559 std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, true)},560 {{CDM::CLibrary, {"g_realloc"}, 2},561 std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},562 {{CDM::CLibrary, {"g_try_realloc"}, 2},563 std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)},564 {{CDM::CLibrary, {"g_realloc_n"}, 3}, &MallocChecker::checkReallocN},565 {{CDM::CLibrary, {"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN},566 };567 568 bool isMemCall(const CallEvent &Call) const;569 bool hasOwnershipReturns(const CallEvent &Call) const;570 bool hasOwnershipTakesHolds(const CallEvent &Call) const;571 void reportTaintBug(StringRef Msg, ProgramStateRef State, CheckerContext &C,572 llvm::ArrayRef<SymbolRef> TaintedSyms,573 AllocationFamily Family) const;574 575 void checkTaintedness(CheckerContext &C, const CallEvent &Call,576 const SVal SizeSVal, ProgramStateRef State,577 AllocationFamily Family) const;578 579 // TODO: Remove mutable by moving the initializtaion to the registry function.580 mutable std::optional<uint64_t> KernelZeroFlagVal;581 582 using KernelZeroSizePtrValueTy = std::optional<int>;583 /// Store the value of macro called `ZERO_SIZE_PTR`.584 /// The value is initialized at first use, before first use the outer585 /// Optional is empty, afterwards it contains another Optional that indicates586 /// if the macro value could be determined, and if yes the value itself.587 mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;588 589 /// Process C++ operator new()'s allocation, which is the part of C++590 /// new-expression that goes before the constructor.591 [[nodiscard]] ProgramStateRef592 processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C,593 AllocationFamily Family) const;594 595 /// Perform a zero-allocation check.596 ///597 /// \param [in] Call The expression that allocates memory.598 /// \param [in] IndexOfSizeArg Index of the argument that specifies the size599 /// of the memory that needs to be allocated. E.g. for malloc, this would be600 /// 0.601 /// \param [in] RetVal Specifies the newly allocated pointer value;602 /// if unspecified, the value of expression \p E is used.603 [[nodiscard]] static ProgramStateRef604 ProcessZeroAllocCheck(CheckerContext &C, const CallEvent &Call,605 const unsigned IndexOfSizeArg, ProgramStateRef State,606 std::optional<SVal> RetVal = std::nullopt);607 608 /// Model functions with the ownership_returns attribute.609 ///610 /// User-defined function may have the ownership_returns attribute, which611 /// annotates that the function returns with an object that was allocated on612 /// the heap, and passes the ownertship to the callee.613 ///614 /// void __attribute((ownership_returns(malloc, 1))) *my_malloc(size_t);615 ///616 /// It has two parameters:617 /// - first: name of the resource (e.g. 'malloc')618 /// - (OPTIONAL) second: size of the allocated region619 ///620 /// \param [in] Call The expression that allocates memory.621 /// \param [in] Att The ownership_returns attribute.622 /// \param [in] State The \c ProgramState right before allocation.623 /// \returns The ProgramState right after allocation.624 [[nodiscard]] ProgramStateRef625 MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,626 const OwnershipAttr *Att, ProgramStateRef State) const;627 /// Models memory allocation.628 ///629 /// \param [in] C Checker context.630 /// \param [in] Call The expression that allocates memory.631 /// \param [in] State The \c ProgramState right before allocation.632 /// \param [in] isAlloca Is the allocation function alloca-like633 /// \returns The ProgramState with returnValue bound634 [[nodiscard]] ProgramStateRef MallocBindRetVal(CheckerContext &C,635 const CallEvent &Call,636 ProgramStateRef State,637 bool isAlloca) const;638 639 /// Models memory allocation.640 ///641 /// \param [in] Call The expression that allocates memory.642 /// \param [in] SizeEx Size of the memory that needs to be allocated.643 /// \param [in] Init The value the allocated memory needs to be initialized.644 /// with. For example, \c calloc initializes the allocated memory to 0,645 /// malloc leaves it undefined.646 /// \param [in] State The \c ProgramState right before allocation.647 /// \returns The ProgramState right after allocation.648 [[nodiscard]] ProgramStateRef649 MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,650 SVal Init, ProgramStateRef State, AllocationFamily Family) const;651 652 /// Models memory allocation.653 ///654 /// \param [in] Call The expression that allocates memory.655 /// \param [in] Size Size of the memory that needs to be allocated.656 /// \param [in] Init The value the allocated memory needs to be initialized.657 /// with. For example, \c calloc initializes the allocated memory to 0,658 /// malloc leaves it undefined.659 /// \param [in] State The \c ProgramState right before allocation.660 /// \returns The ProgramState right after allocation.661 [[nodiscard]] ProgramStateRef MallocMemAux(CheckerContext &C,662 const CallEvent &Call, SVal Size,663 SVal Init, ProgramStateRef State,664 AllocationFamily Family) const;665 666 // Check if this malloc() for special flags. At present that means M_ZERO or667 // __GFP_ZERO (in which case, treat it like calloc).668 [[nodiscard]] std::optional<ProgramStateRef>669 performKernelMalloc(const CallEvent &Call, CheckerContext &C,670 const ProgramStateRef &State) const;671 672 /// Model functions with the ownership_takes and ownership_holds attributes.673 ///674 /// User-defined function may have the ownership_takes and/or ownership_holds675 /// attributes, which annotates that the function frees the memory passed as a676 /// parameter.677 ///678 /// void __attribute((ownership_takes(malloc, 1))) my_free(void *);679 /// void __attribute((ownership_holds(malloc, 1))) my_hold(void *);680 ///681 /// They have two parameters:682 /// - first: name of the resource (e.g. 'malloc')683 /// - second: index of the parameter the attribute applies to684 ///685 /// \param [in] Call The expression that frees memory.686 /// \param [in] Att The ownership_takes or ownership_holds attribute.687 /// \param [in] State The \c ProgramState right before allocation.688 /// \returns The ProgramState right after deallocation.689 [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C,690 const CallEvent &Call,691 const OwnershipAttr *Att,692 ProgramStateRef State) const;693 694 /// Models memory deallocation.695 ///696 /// \param [in] Call The expression that frees memory.697 /// \param [in] State The \c ProgramState right before allocation.698 /// \param [in] Num Index of the argument that needs to be freed. This is699 /// normally 0, but for custom free functions it may be different.700 /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds701 /// attribute.702 /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known703 /// to have been allocated, or in other words, the symbol to be freed was704 /// registered as allocated by this checker. In the following case, \c ptr705 /// isn't known to be allocated.706 /// void Haha(int *ptr) {707 /// ptr = realloc(ptr, 67);708 /// // ...709 /// }710 /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function711 /// we're modeling returns with Null on failure.712 /// \returns The ProgramState right after deallocation.713 [[nodiscard]] ProgramStateRef714 FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State,715 unsigned Num, bool Hold, bool &IsKnownToBeAllocated,716 AllocationFamily Family, bool ReturnsNullOnFailure = false) const;717 718 /// Models memory deallocation.719 ///720 /// \param [in] ArgExpr The variable who's pointee needs to be freed.721 /// \param [in] Call The expression that frees the memory.722 /// \param [in] State The \c ProgramState right before allocation.723 /// normally 0, but for custom free functions it may be different.724 /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds725 /// attribute.726 /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known727 /// to have been allocated, or in other words, the symbol to be freed was728 /// registered as allocated by this checker. In the following case, \c ptr729 /// isn't known to be allocated.730 /// void Haha(int *ptr) {731 /// ptr = realloc(ptr, 67);732 /// // ...733 /// }734 /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function735 /// we're modeling returns with Null on failure.736 /// \param [in] ArgValOpt Optional value to use for the argument instead of737 /// the one obtained from ArgExpr.738 /// \returns The ProgramState right after deallocation.739 [[nodiscard]] ProgramStateRef740 FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,741 ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,742 AllocationFamily Family, bool ReturnsNullOnFailure = false,743 std::optional<SVal> ArgValOpt = {}) const;744 745 // TODO: Needs some refactoring, as all other deallocation modeling746 // functions are suffering from out parameters and messy code due to how747 // realloc is handled.748 //749 /// Models memory reallocation.750 ///751 /// \param [in] Call The expression that reallocated memory752 /// \param [in] ShouldFreeOnFail Whether if reallocation fails, the supplied753 /// memory should be freed.754 /// \param [in] State The \c ProgramState right before reallocation.755 /// \param [in] SuffixWithN Whether the reallocation function we're modeling756 /// has an '_n' suffix, such as g_realloc_n.757 /// \returns The ProgramState right after reallocation.758 [[nodiscard]] ProgramStateRef759 ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail,760 ProgramStateRef State, AllocationFamily Family,761 bool SuffixWithN = false) const;762 763 /// Evaluates the buffer size that needs to be allocated.764 ///765 /// \param [in] Blocks The amount of blocks that needs to be allocated.766 /// \param [in] BlockBytes The size of a block.767 /// \returns The symbolic value of \p Blocks * \p BlockBytes.768 [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C,769 const Expr *Blocks,770 const Expr *BlockBytes);771 772 /// Models zero initialized array allocation.773 ///774 /// \param [in] Call The expression that reallocated memory775 /// \param [in] State The \c ProgramState right before reallocation.776 /// \returns The ProgramState right after allocation.777 [[nodiscard]] ProgramStateRef CallocMem(CheckerContext &C,778 const CallEvent &Call,779 ProgramStateRef State) const;780 781 /// See if deallocation happens in a suspicious context. If so, escape the782 /// pointers that otherwise would have been deallocated and return true.783 bool suppressDeallocationsInSuspiciousContexts(const CallEvent &Call,784 CheckerContext &C) const;785 786 /// If in \p S \p Sym is used, check whether \p Sym was already freed.787 bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;788 789 /// If in \p S \p Sym is used, check whether \p Sym was allocated as a zero790 /// sized memory region.791 void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,792 const Stmt *S) const;793 794 /// Check if the function is known to free memory, or if it is795 /// "interesting" and should be modeled explicitly.796 ///797 /// \param [out] EscapingSymbol A function might not free memory in general,798 /// but could be known to free a particular symbol. In this case, false is799 /// returned and the single escaping symbol is returned through the out800 /// parameter.801 ///802 /// We assume that pointers do not escape through calls to system functions803 /// not handled by this checker.804 bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,805 ProgramStateRef State,806 SymbolRef &EscapingSymbol) const;807 808 /// Implementation of the checkPointerEscape callbacks.809 [[nodiscard]] ProgramStateRef810 checkPointerEscapeAux(ProgramStateRef State,811 const InvalidatedSymbols &Escaped,812 const CallEvent *Call, PointerEscapeKind Kind,813 bool IsConstPointerEscape) const;814 815 // Implementation of the checkPreStmt and checkEndFunction callbacks.816 void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const;817 818 ///@{819 /// Returns a pointer to the checker frontend corresponding to the given820 /// family or symbol. The template argument T may be either CheckerFamily or821 /// a BUGTYPE_PROVIDER class; in the latter case the query is restricted to822 /// frontends that descend from that PROVIDER class (i.e. can emit that bug823 /// type). Note that this may return a frontend which is disabled.824 template <class T>825 const T *getRelevantFrontendAs(AllocationFamily Family) const;826 827 template <class T>828 const T *getRelevantFrontendAs(CheckerContext &C, SymbolRef Sym) const;829 ///@}830 static bool SummarizeValue(raw_ostream &os, SVal V);831 static bool SummarizeRegion(ProgramStateRef State, raw_ostream &os,832 const MemRegion *MR);833 834 void HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal, SourceRange Range,835 const Expr *DeallocExpr,836 AllocationFamily Family) const;837 838 void HandleFreeAlloca(CheckerContext &C, SVal ArgVal,839 SourceRange Range) const;840 841 void HandleMismatchedDealloc(CheckerContext &C, SourceRange Range,842 const Expr *DeallocExpr, const RefState *RS,843 SymbolRef Sym, bool OwnershipTransferred) const;844 845 void HandleOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,846 const Expr *DeallocExpr, AllocationFamily Family,847 const Expr *AllocExpr = nullptr) const;848 849 void HandleUseAfterFree(CheckerContext &C, SourceRange Range,850 SymbolRef Sym) const;851 852 void HandleDoubleFree(CheckerContext &C, SourceRange Range, bool Released,853 SymbolRef Sym, SymbolRef PrevSym) const;854 855 void HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,856 SymbolRef Sym) const;857 858 void HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal, SourceRange Range,859 const Expr *FreeExpr,860 AllocationFamily Family) const;861 862 /// Find the location of the allocation for Sym on the path leading to the863 /// exploded node N.864 static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,865 CheckerContext &C);866 867 void HandleLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;868 869 /// Test if value in ArgVal equals to value in macro `ZERO_SIZE_PTR`.870 bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,871 SVal ArgVal) const;872};873} // end anonymous namespace874 875//===----------------------------------------------------------------------===//876// Definition of NoOwnershipChangeVisitor.877//===----------------------------------------------------------------------===//878 879namespace {880class NoMemOwnershipChangeVisitor final : public NoOwnershipChangeVisitor {881protected:882 /// Syntactically checks whether the callee is a deallocating function. Since883 /// we have no path-sensitive information on this call (we would need a884 /// CallEvent instead of a CallExpr for that), its possible that a885 /// deallocation function was called indirectly through a function pointer,886 /// but we are not able to tell, so this is a best effort analysis.887 /// See namespace `memory_passed_to_fn_call_free_through_fn_ptr` in888 /// clang/test/Analysis/NewDeleteLeaks.cpp.889 bool isFreeingCallAsWritten(const CallExpr &Call) const {890 const auto *MallocChk = static_cast<const MallocChecker *>(&Checker);891 if (MallocChk->FreeingMemFnMap.lookupAsWritten(Call) ||892 MallocChk->ReallocatingMemFnMap.lookupAsWritten(Call))893 return true;894 895 if (const auto *Func =896 llvm::dyn_cast_or_null<FunctionDecl>(Call.getCalleeDecl()))897 return MallocChecker::isFreeingOwnershipAttrCall(Func);898 899 return false;900 }901 902 bool hasResourceStateChanged(ProgramStateRef CallEnterState,903 ProgramStateRef CallExitEndState) final {904 return CallEnterState->get<RegionState>(Sym) !=905 CallExitEndState->get<RegionState>(Sym);906 }907 908 /// Heuristically guess whether the callee intended to free memory. This is909 /// done syntactically, because we are trying to argue about alternative910 /// paths of execution, and as a consequence we don't have path-sensitive911 /// information.912 bool doesFnIntendToHandleOwnership(const Decl *Callee,913 ASTContext &ACtx) final {914 const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);915 916 // Given that the stack frame was entered, the body should always be917 // theoretically obtainable. In case of body farms, the synthesized body918 // is not attached to declaration, thus triggering the '!FD->hasBody()'919 // branch. That said, would a synthesized body ever intend to handle920 // ownership? As of today they don't. And if they did, how would we921 // put notes inside it, given that it doesn't match any source locations?922 if (!FD || !FD->hasBody())923 return false;924 using namespace clang::ast_matchers;925 926 auto Matches = match(findAll(stmt(anyOf(cxxDeleteExpr().bind("delete"),927 callExpr().bind("call")))),928 *FD->getBody(), ACtx);929 for (BoundNodes Match : Matches) {930 if (Match.getNodeAs<CXXDeleteExpr>("delete"))931 return true;932 933 if (const auto *Call = Match.getNodeAs<CallExpr>("call"))934 if (isFreeingCallAsWritten(*Call))935 return true;936 }937 // TODO: Ownership might change with an attempt to store the allocated938 // memory, not only through deallocation. Check for attempted stores as939 // well.940 return false;941 }942 943 PathDiagnosticPieceRef emitNote(const ExplodedNode *N) final {944 PathDiagnosticLocation L = PathDiagnosticLocation::create(945 N->getLocation(),946 N->getState()->getStateManager().getContext().getSourceManager());947 return std::make_shared<PathDiagnosticEventPiece>(948 L, "Returning without deallocating memory or storing the pointer for "949 "later deallocation");950 }951 952public:953 NoMemOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)954 : NoOwnershipChangeVisitor(Sym, Checker) {}955 956 void Profile(llvm::FoldingSetNodeID &ID) const override {957 static int Tag = 0;958 ID.AddPointer(&Tag);959 ID.AddPointer(Sym);960 }961};962 963} // end anonymous namespace964 965//===----------------------------------------------------------------------===//966// Definition of MallocBugVisitor.967//===----------------------------------------------------------------------===//968 969namespace {970/// The bug visitor which allows us to print extra diagnostics along the971/// BugReport path. For example, showing the allocation site of the leaked972/// region.973class MallocBugVisitor final : public BugReporterVisitor {974protected:975 enum NotificationMode { Normal, ReallocationFailed };976 977 // The allocated region symbol tracked by the main analysis.978 SymbolRef Sym;979 980 // The mode we are in, i.e. what kind of diagnostics will be emitted.981 NotificationMode Mode;982 983 // A symbol from when the primary region should have been reallocated.984 SymbolRef FailedReallocSymbol;985 986 // A release function stack frame in which memory was released. Used for987 // miscellaneous false positive suppression.988 const StackFrameContext *ReleaseFunctionLC;989 990 bool IsLeak;991 992public:993 MallocBugVisitor(SymbolRef S, bool isLeak = false)994 : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr),995 ReleaseFunctionLC(nullptr), IsLeak(isLeak) {}996 997 static void *getTag() {998 static int Tag = 0;999 return &Tag;1000 }1001 1002 void Profile(llvm::FoldingSetNodeID &ID) const override {1003 ID.AddPointer(getTag());1004 ID.AddPointer(Sym);1005 }1006 1007 /// Did not track -> allocated. Other state (released) -> allocated.1008 static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev,1009 const Stmt *Stmt) {1010 return (isa_and_nonnull<CallExpr, CXXNewExpr>(Stmt) &&1011 (RSCurr &&1012 (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&1013 (!RSPrev ||1014 !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero())));1015 }1016 1017 /// Did not track -> released. Other state (allocated) -> released.1018 /// The statement associated with the release might be missing.1019 static inline bool isReleased(const RefState *RSCurr, const RefState *RSPrev,1020 const Stmt *Stmt) {1021 bool IsReleased =1022 (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased());1023 assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) ||1024 (!Stmt && RSCurr->getAllocationFamily().Kind == AF_InnerBuffer));1025 return IsReleased;1026 }1027 1028 /// Did not track -> relinquished. Other state (allocated) -> relinquished.1029 static inline bool isRelinquished(const RefState *RSCurr,1030 const RefState *RSPrev, const Stmt *Stmt) {1031 return (1032 isa_and_nonnull<CallExpr, ObjCMessageExpr, ObjCPropertyRefExpr>(Stmt) &&1033 (RSCurr && RSCurr->isRelinquished()) &&1034 (!RSPrev || !RSPrev->isRelinquished()));1035 }1036 1037 /// If the expression is not a call, and the state change is1038 /// released -> allocated, it must be the realloc return value1039 /// check. If we have to handle more cases here, it might be cleaner just1040 /// to track this extra bit in the state itself.1041 static inline bool hasReallocFailed(const RefState *RSCurr,1042 const RefState *RSPrev,1043 const Stmt *Stmt) {1044 return ((!isa_and_nonnull<CallExpr>(Stmt)) &&1045 (RSCurr &&1046 (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&1047 (RSPrev &&1048 !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero())));1049 }1050 1051 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,1052 BugReporterContext &BRC,1053 PathSensitiveBugReport &BR) override;1054 1055 PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,1056 const ExplodedNode *EndPathNode,1057 PathSensitiveBugReport &BR) override {1058 if (!IsLeak)1059 return nullptr;1060 1061 PathDiagnosticLocation L = BR.getLocation();1062 // Do not add the statement itself as a range in case of leak.1063 return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),1064 false);1065 }1066 1067private:1068 class StackHintGeneratorForReallocationFailed1069 : public StackHintGeneratorForSymbol {1070 public:1071 StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)1072 : StackHintGeneratorForSymbol(S, M) {}1073 1074 std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) override {1075 // Printed parameters start at 1, not 0.1076 ++ArgIndex;1077 1078 SmallString<200> buf;1079 llvm::raw_svector_ostream os(buf);1080 1081 os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)1082 << " parameter failed";1083 1084 return std::string(os.str());1085 }1086 1087 std::string getMessageForReturn(const CallExpr *CallExpr) override {1088 return "Reallocation of returned value failed";1089 }1090 };1091};1092} // end anonymous namespace1093 1094// A map from the freed symbol to the symbol representing the return value of1095// the free function.1096REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)1097 1098namespace {1099class StopTrackingCallback final : public SymbolVisitor {1100 ProgramStateRef state;1101 1102public:1103 StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}1104 ProgramStateRef getState() const { return state; }1105 1106 bool VisitSymbol(SymbolRef sym) override {1107 state = state->remove<RegionState>(sym);1108 return true;1109 }1110};1111 1112/// EscapeTrackedCallback - A SymbolVisitor that marks allocated symbols as1113/// escaped.1114///1115/// This visitor is used to suppress false positive leak reports when smart1116/// pointers are nested in temporary objects passed by value to functions. When1117/// the analyzer can't see the destructor calls for temporary objects, it may1118/// incorrectly report leaks for memory that will be properly freed by the smart1119/// pointer destructors.1120///1121/// The visitor traverses reachable symbols from a given set of memory regions1122/// (typically smart pointer field regions) and marks any allocated symbols as1123/// escaped. Escaped symbols are not reported as leaks by checkDeadSymbols.1124class EscapeTrackedCallback final : public SymbolVisitor {1125 ProgramStateRef State;1126 1127 explicit EscapeTrackedCallback(ProgramStateRef S) : State(std::move(S)) {}1128 1129public:1130 bool VisitSymbol(SymbolRef Sym) override {1131 if (const RefState *RS = State->get<RegionState>(Sym)) {1132 if (RS->isAllocated() || RS->isAllocatedOfSizeZero()) {1133 State = State->set<RegionState>(Sym, RefState::getEscaped(RS));1134 }1135 }1136 return true;1137 }1138 1139 /// Escape tracked regions reachable from the given roots.1140 static ProgramStateRef1141 EscapeTrackedRegionsReachableFrom(ArrayRef<const MemRegion *> Roots,1142 ProgramStateRef State) {1143 if (Roots.empty())1144 return State;1145 1146 // scanReachableSymbols is expensive, so we use a single visitor for all1147 // roots1148 SmallVector<const MemRegion *, 10> Regions;1149 EscapeTrackedCallback Visitor(State);1150 for (const MemRegion *R : Roots) {1151 Regions.push_back(R);1152 }1153 State->scanReachableSymbols(Regions, Visitor);1154 return Visitor.State;1155 }1156 1157 friend class SymbolVisitor;1158};1159} // end anonymous namespace1160 1161static bool isStandardNew(const FunctionDecl *FD) {1162 if (!FD)1163 return false;1164 1165 OverloadedOperatorKind Kind = FD->getOverloadedOperator();1166 if (Kind != OO_New && Kind != OO_Array_New)1167 return false;1168 1169 // This is standard if and only if it's not defined in a user file.1170 SourceLocation L = FD->getLocation();1171 // If the header for operator delete is not included, it's still defined1172 // in an invalid source location. Check to make sure we don't crash.1173 return !L.isValid() ||1174 FD->getASTContext().getSourceManager().isInSystemHeader(L);1175}1176 1177static bool isStandardDelete(const FunctionDecl *FD) {1178 if (!FD)1179 return false;1180 1181 OverloadedOperatorKind Kind = FD->getOverloadedOperator();1182 if (Kind != OO_Delete && Kind != OO_Array_Delete)1183 return false;1184 1185 bool HasBody = FD->hasBody(); // Prefer using the definition.1186 1187 // This is standard if and only if it's not defined in a user file.1188 SourceLocation L = FD->getLocation();1189 1190 // If the header for operator delete is not included, it's still defined1191 // in an invalid source location. Check to make sure we don't crash.1192 const auto &SM = FD->getASTContext().getSourceManager();1193 return L.isInvalid() || (!HasBody && SM.isInSystemHeader(L));1194}1195 1196//===----------------------------------------------------------------------===//1197// Methods of MallocChecker and MallocBugVisitor.1198//===----------------------------------------------------------------------===//1199 1200bool MallocChecker::isFreeingOwnershipAttrCall(const CallEvent &Call) {1201 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());1202 1203 return Func && isFreeingOwnershipAttrCall(Func);1204}1205 1206bool MallocChecker::isFreeingOwnershipAttrCall(const FunctionDecl *Func) {1207 if (Func->hasAttrs()) {1208 for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {1209 OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();1210 if (OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds)1211 return true;1212 }1213 }1214 return false;1215}1216 1217bool MallocChecker::isFreeingCall(const CallEvent &Call) const {1218 if (FreeingMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))1219 return true;1220 1221 return isFreeingOwnershipAttrCall(Call);1222}1223 1224bool MallocChecker::isAllocatingOwnershipAttrCall(const CallEvent &Call) {1225 const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());1226 1227 return Func && isAllocatingOwnershipAttrCall(Func);1228}1229 1230bool MallocChecker::isAllocatingOwnershipAttrCall(const FunctionDecl *Func) {1231 for (const auto *I : Func->specific_attrs<OwnershipAttr>()) {1232 if (I->getOwnKind() == OwnershipAttr::Returns)1233 return true;1234 }1235 1236 return false;1237}1238 1239bool MallocChecker::isMemCall(const CallEvent &Call) const {1240 if (FreeingMemFnMap.lookup(Call) || AllocatingMemFnMap.lookup(Call) ||1241 AllocaMemFnMap.lookup(Call) || ReallocatingMemFnMap.lookup(Call))1242 return true;1243 1244 if (!ShouldIncludeOwnershipAnnotatedFunctions)1245 return false;1246 1247 const auto *Func = dyn_cast<FunctionDecl>(Call.getDecl());1248 return Func && Func->hasAttr<OwnershipAttr>();1249}1250 1251std::optional<ProgramStateRef>1252MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,1253 const ProgramStateRef &State) const {1254 // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:1255 //1256 // void *malloc(unsigned long size, struct malloc_type *mtp, int flags);1257 //1258 // One of the possible flags is M_ZERO, which means 'give me back an1259 // allocation which is already zeroed', like calloc.1260 1261 // 2-argument kmalloc(), as used in the Linux kernel:1262 //1263 // void *kmalloc(size_t size, gfp_t flags);1264 //1265 // Has the similar flag value __GFP_ZERO.1266 1267 // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some1268 // code could be shared.1269 1270 ASTContext &Ctx = C.getASTContext();1271 llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();1272 1273 if (!KernelZeroFlagVal) {1274 switch (OS) {1275 case llvm::Triple::FreeBSD:1276 KernelZeroFlagVal = 0x0100;1277 break;1278 case llvm::Triple::NetBSD:1279 KernelZeroFlagVal = 0x0002;1280 break;1281 case llvm::Triple::OpenBSD:1282 KernelZeroFlagVal = 0x0008;1283 break;1284 case llvm::Triple::Linux:1285 // __GFP_ZERO1286 KernelZeroFlagVal = 0x8000;1287 break;1288 default:1289 // FIXME: We need a more general way of getting the M_ZERO value.1290 // See also: O_CREAT in UnixAPIChecker.cpp.1291 1292 // Fall back to normal malloc behavior on platforms where we don't1293 // know M_ZERO.1294 return std::nullopt;1295 }1296 }1297 1298 // We treat the last argument as the flags argument, and callers fall-back to1299 // normal malloc on a None return. This works for the FreeBSD kernel malloc1300 // as well as Linux kmalloc.1301 if (Call.getNumArgs() < 2)1302 return std::nullopt;1303 1304 const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1);1305 const SVal V = C.getSVal(FlagsEx);1306 if (!isa<NonLoc>(V)) {1307 // The case where 'V' can be a location can only be due to a bad header,1308 // so in this case bail out.1309 return std::nullopt;1310 }1311 1312 NonLoc Flags = V.castAs<NonLoc>();1313 NonLoc ZeroFlag = C.getSValBuilder()1314 .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType())1315 .castAs<NonLoc>();1316 SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,1317 Flags, ZeroFlag,1318 FlagsEx->getType());1319 if (MaskedFlagsUC.isUnknownOrUndef())1320 return std::nullopt;1321 DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();1322 1323 // Check if maskedFlags is non-zero.1324 ProgramStateRef TrueState, FalseState;1325 std::tie(TrueState, FalseState) = State->assume(MaskedFlags);1326 1327 // If M_ZERO is set, treat this like calloc (initialized).1328 if (TrueState && !FalseState) {1329 SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);1330 return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState,1331 AllocationFamily(AF_Malloc));1332 }1333 1334 return std::nullopt;1335}1336 1337SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,1338 const Expr *BlockBytes) {1339 SValBuilder &SB = C.getSValBuilder();1340 SVal BlocksVal = C.getSVal(Blocks);1341 SVal BlockBytesVal = C.getSVal(BlockBytes);1342 ProgramStateRef State = C.getState();1343 SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal,1344 SB.getContext().getCanonicalSizeType());1345 return TotalSize;1346}1347 1348void MallocChecker::checkBasicAlloc(ProgramStateRef State,1349 const CallEvent &Call,1350 CheckerContext &C) const {1351 State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,1352 AllocationFamily(AF_Malloc));1353 State = ProcessZeroAllocCheck(C, Call, 0, State);1354 C.addTransition(State);1355}1356 1357void MallocChecker::checkKernelMalloc(ProgramStateRef State,1358 const CallEvent &Call,1359 CheckerContext &C) const {1360 std::optional<ProgramStateRef> MaybeState =1361 performKernelMalloc(Call, C, State);1362 if (MaybeState)1363 State = *MaybeState;1364 else1365 State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,1366 AllocationFamily(AF_Malloc));1367 C.addTransition(State);1368}1369 1370static bool isStandardRealloc(const CallEvent &Call) {1371 const FunctionDecl *FD = dyn_cast<FunctionDecl>(Call.getDecl());1372 assert(FD);1373 ASTContext &AC = FD->getASTContext();1374 return AC.hasSameType(FD->getDeclaredReturnType(), AC.VoidPtrTy) &&1375 AC.hasSameType(FD->getParamDecl(0)->getType(), AC.VoidPtrTy) &&1376 AC.hasSameType(FD->getParamDecl(1)->getType(), AC.getSizeType());1377}1378 1379static bool isGRealloc(const CallEvent &Call) {1380 const FunctionDecl *FD = dyn_cast<FunctionDecl>(Call.getDecl());1381 assert(FD);1382 ASTContext &AC = FD->getASTContext();1383 1384 return AC.hasSameType(FD->getDeclaredReturnType(), AC.VoidPtrTy) &&1385 AC.hasSameType(FD->getParamDecl(0)->getType(), AC.VoidPtrTy) &&1386 AC.hasSameType(FD->getParamDecl(1)->getType(), AC.UnsignedLongTy);1387}1388 1389void MallocChecker::checkRealloc(ProgramStateRef State, const CallEvent &Call,1390 CheckerContext &C,1391 bool ShouldFreeOnFail) const {1392 // Ignore calls to functions whose type does not match the expected type of1393 // either the standard realloc or g_realloc from GLib.1394 // FIXME: Should we perform this kind of checking consistently for each1395 // function? If yes, then perhaps extend the `CallDescription` interface to1396 // handle this.1397 if (!isStandardRealloc(Call) && !isGRealloc(Call))1398 return;1399 1400 State = ReallocMemAux(C, Call, ShouldFreeOnFail, State,1401 AllocationFamily(AF_Malloc));1402 State = ProcessZeroAllocCheck(C, Call, 1, State);1403 C.addTransition(State);1404}1405 1406void MallocChecker::checkCalloc(ProgramStateRef State, const CallEvent &Call,1407 CheckerContext &C) const {1408 State = CallocMem(C, Call, State);1409 State = ProcessZeroAllocCheck(C, Call, 0, State);1410 State = ProcessZeroAllocCheck(C, Call, 1, State);1411 C.addTransition(State);1412}1413 1414void MallocChecker::checkFree(ProgramStateRef State, const CallEvent &Call,1415 CheckerContext &C) const {1416 bool IsKnownToBeAllocatedMemory = false;1417 if (suppressDeallocationsInSuspiciousContexts(Call, C))1418 return;1419 State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,1420 AllocationFamily(AF_Malloc));1421 C.addTransition(State);1422}1423 1424void MallocChecker::checkAlloca(ProgramStateRef State, const CallEvent &Call,1425 CheckerContext &C) const {1426 State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,1427 AllocationFamily(AF_Alloca));1428 State = ProcessZeroAllocCheck(C, Call, 0, State);1429 C.addTransition(State);1430}1431 1432void MallocChecker::checkStrdup(ProgramStateRef State, const CallEvent &Call,1433 CheckerContext &C) const {1434 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());1435 if (!CE)1436 return;1437 State = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State,1438 AllocationFamily(AF_Malloc));1439 1440 C.addTransition(State);1441}1442 1443void MallocChecker::checkIfNameIndex(ProgramStateRef State,1444 const CallEvent &Call,1445 CheckerContext &C) const {1446 // Should we model this differently? We can allocate a fixed number of1447 // elements with zeros in the last one.1448 State = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State,1449 AllocationFamily(AF_IfNameIndex));1450 1451 C.addTransition(State);1452}1453 1454void MallocChecker::checkIfFreeNameIndex(ProgramStateRef State,1455 const CallEvent &Call,1456 CheckerContext &C) const {1457 bool IsKnownToBeAllocatedMemory = false;1458 State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,1459 AllocationFamily(AF_IfNameIndex));1460 C.addTransition(State);1461}1462 1463static const Expr *getPlacementNewBufferArg(const CallExpr *CE,1464 const FunctionDecl *FD) {1465 // Checking for signature:1466 // void* operator new ( std::size_t count, void* ptr );1467 // void* operator new[]( std::size_t count, void* ptr );1468 if (CE->getNumArgs() != 2 || (FD->getOverloadedOperator() != OO_New &&1469 FD->getOverloadedOperator() != OO_Array_New))1470 return nullptr;1471 auto BuffType = FD->getParamDecl(1)->getType();1472 if (BuffType.isNull() || !BuffType->isVoidPointerType())1473 return nullptr;1474 return CE->getArg(1);1475}1476 1477void MallocChecker::checkCXXNewOrCXXDelete(ProgramStateRef State,1478 const CallEvent &Call,1479 CheckerContext &C) const {1480 bool IsKnownToBeAllocatedMemory = false;1481 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());1482 if (!CE)1483 return;1484 1485 assert(isStandardNewDelete(Call));1486 1487 // Process direct calls to operator new/new[]/delete/delete[] functions1488 // as distinct from new/new[]/delete/delete[] expressions that are1489 // processed by the checkPostStmt callbacks for CXXNewExpr and1490 // CXXDeleteExpr.1491 const FunctionDecl *FD = C.getCalleeDecl(CE);1492 if (const auto *BufArg = getPlacementNewBufferArg(CE, FD)) {1493 // Placement new does not allocate memory1494 auto RetVal = State->getSVal(BufArg, Call.getLocationContext());1495 State = State->BindExpr(CE, C.getLocationContext(), RetVal);1496 C.addTransition(State);1497 return;1498 }1499 1500 switch (FD->getOverloadedOperator()) {1501 case OO_New:1502 State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,1503 AllocationFamily(AF_CXXNew));1504 State = ProcessZeroAllocCheck(C, Call, 0, State);1505 break;1506 case OO_Array_New:1507 State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State,1508 AllocationFamily(AF_CXXNewArray));1509 State = ProcessZeroAllocCheck(C, Call, 0, State);1510 break;1511 case OO_Delete:1512 State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,1513 AllocationFamily(AF_CXXNew));1514 break;1515 case OO_Array_Delete:1516 State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory,1517 AllocationFamily(AF_CXXNewArray));1518 break;1519 default:1520 assert(false && "not a new/delete operator");1521 return;1522 }1523 1524 C.addTransition(State);1525}1526 1527void MallocChecker::checkGMalloc0(ProgramStateRef State, const CallEvent &Call,1528 CheckerContext &C) const {1529 SValBuilder &svalBuilder = C.getSValBuilder();1530 SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);1531 State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State,1532 AllocationFamily(AF_Malloc));1533 State = ProcessZeroAllocCheck(C, Call, 0, State);1534 C.addTransition(State);1535}1536 1537void MallocChecker::checkGMemdup(ProgramStateRef State, const CallEvent &Call,1538 CheckerContext &C) const {1539 State = MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State,1540 AllocationFamily(AF_Malloc));1541 State = ProcessZeroAllocCheck(C, Call, 1, State);1542 C.addTransition(State);1543}1544 1545void MallocChecker::checkGMallocN(ProgramStateRef State, const CallEvent &Call,1546 CheckerContext &C) const {1547 SVal Init = UndefinedVal();1548 SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));1549 State = MallocMemAux(C, Call, TotalSize, Init, State,1550 AllocationFamily(AF_Malloc));1551 State = ProcessZeroAllocCheck(C, Call, 0, State);1552 State = ProcessZeroAllocCheck(C, Call, 1, State);1553 C.addTransition(State);1554}1555 1556void MallocChecker::checkGMallocN0(ProgramStateRef State, const CallEvent &Call,1557 CheckerContext &C) const {1558 SValBuilder &SB = C.getSValBuilder();1559 SVal Init = SB.makeZeroVal(SB.getContext().CharTy);1560 SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));1561 State = MallocMemAux(C, Call, TotalSize, Init, State,1562 AllocationFamily(AF_Malloc));1563 State = ProcessZeroAllocCheck(C, Call, 0, State);1564 State = ProcessZeroAllocCheck(C, Call, 1, State);1565 C.addTransition(State);1566}1567 1568static bool isFromStdNamespace(const CallEvent &Call) {1569 const Decl *FD = Call.getDecl();1570 assert(FD && "a CallDescription cannot match a call without a Decl");1571 return FD->isInStdNamespace();1572}1573 1574void MallocChecker::preGetDelimOrGetLine(ProgramStateRef State,1575 const CallEvent &Call,1576 CheckerContext &C) const {1577 // Discard calls to the C++ standard library function std::getline(), which1578 // is completely unrelated to the POSIX getline() that we're checking.1579 if (isFromStdNamespace(Call))1580 return;1581 1582 const auto LinePtr = getPointeeVal(Call.getArgSVal(0), State);1583 if (!LinePtr)1584 return;1585 1586 // FreeMemAux takes IsKnownToBeAllocated as an output parameter, and it will1587 // be true after the call if the symbol was registered by this checker.1588 // We do not need this value here, as FreeMemAux will take care1589 // of reporting any violation of the preconditions.1590 bool IsKnownToBeAllocated = false;1591 State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false,1592 IsKnownToBeAllocated, AllocationFamily(AF_Malloc), false,1593 LinePtr);1594 if (State)1595 C.addTransition(State);1596}1597 1598void MallocChecker::checkGetDelimOrGetLine(ProgramStateRef State,1599 const CallEvent &Call,1600 CheckerContext &C) const {1601 // Discard calls to the C++ standard library function std::getline(), which1602 // is completely unrelated to the POSIX getline() that we're checking.1603 if (isFromStdNamespace(Call))1604 return;1605 1606 // Handle the post-conditions of getline and getdelim:1607 // Register the new conjured value as an allocated buffer.1608 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());1609 if (!CE)1610 return;1611 1612 const auto LinePtrOpt = getPointeeVal(Call.getArgSVal(0), State);1613 const auto SizeOpt = getPointeeVal(Call.getArgSVal(1), State);1614 if (!LinePtrOpt || !SizeOpt || LinePtrOpt->isUnknownOrUndef() ||1615 SizeOpt->isUnknownOrUndef())1616 return;1617 1618 const auto LinePtr = LinePtrOpt->getAs<DefinedSVal>();1619 const auto Size = SizeOpt->getAs<DefinedSVal>();1620 const MemRegion *LinePtrReg = LinePtr->getAsRegion();1621 if (!LinePtrReg)1622 return;1623 1624 State = setDynamicExtent(State, LinePtrReg, *Size);1625 C.addTransition(MallocUpdateRefState(C, CE, State,1626 AllocationFamily(AF_Malloc), *LinePtr));1627}1628 1629void MallocChecker::checkReallocN(ProgramStateRef State, const CallEvent &Call,1630 CheckerContext &C) const {1631 State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State,1632 AllocationFamily(AF_Malloc),1633 /*SuffixWithN=*/true);1634 State = ProcessZeroAllocCheck(C, Call, 1, State);1635 State = ProcessZeroAllocCheck(C, Call, 2, State);1636 C.addTransition(State);1637}1638 1639void MallocChecker::checkOwnershipAttr(ProgramStateRef State,1640 const CallEvent &Call,1641 CheckerContext &C) const {1642 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());1643 if (!CE)1644 return;1645 const FunctionDecl *FD = C.getCalleeDecl(CE);1646 if (!FD)1647 return;1648 if (ShouldIncludeOwnershipAnnotatedFunctions ||1649 MismatchedDeallocatorChecker.isEnabled()) {1650 // Check all the attributes, if there are any.1651 // There can be multiple of these attributes.1652 if (FD->hasAttrs())1653 for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {1654 switch (I->getOwnKind()) {1655 case OwnershipAttr::Returns:1656 State = MallocMemReturnsAttr(C, Call, I, State);1657 break;1658 case OwnershipAttr::Takes:1659 case OwnershipAttr::Holds:1660 State = FreeMemAttr(C, Call, I, State);1661 break;1662 }1663 }1664 }1665 C.addTransition(State);1666}1667 1668bool MallocChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {1669 if (!Call.getOriginExpr())1670 return false;1671 1672 ProgramStateRef State = C.getState();1673 1674 if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) {1675 (*Callback)(this, State, Call, C);1676 return true;1677 }1678 1679 if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) {1680 State = MallocBindRetVal(C, Call, State, false);1681 (*Callback)(this, State, Call, C);1682 return true;1683 }1684 1685 if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) {1686 State = MallocBindRetVal(C, Call, State, false);1687 (*Callback)(this, State, Call, C);1688 return true;1689 }1690 1691 if (isStandardNew(Call)) {1692 State = MallocBindRetVal(C, Call, State, false);1693 checkCXXNewOrCXXDelete(State, Call, C);1694 return true;1695 }1696 1697 if (isStandardDelete(Call)) {1698 checkCXXNewOrCXXDelete(State, Call, C);1699 return true;1700 }1701 1702 if (const CheckFn *Callback = AllocaMemFnMap.lookup(Call)) {1703 State = MallocBindRetVal(C, Call, State, true);1704 (*Callback)(this, State, Call, C);1705 return true;1706 }1707 1708 if (isFreeingOwnershipAttrCall(Call)) {1709 checkOwnershipAttr(State, Call, C);1710 return true;1711 }1712 1713 if (isAllocatingOwnershipAttrCall(Call)) {1714 State = MallocBindRetVal(C, Call, State, false);1715 checkOwnershipAttr(State, Call, C);1716 return true;1717 }1718 1719 return false;1720}1721 1722// Performs a 0-sized allocations check.1723ProgramStateRef MallocChecker::ProcessZeroAllocCheck(1724 CheckerContext &C, const CallEvent &Call, const unsigned IndexOfSizeArg,1725 ProgramStateRef State, std::optional<SVal> RetVal) {1726 if (!State)1727 return nullptr;1728 1729 const Expr *Arg = nullptr;1730 1731 if (const CallExpr *CE = dyn_cast<CallExpr>(Call.getOriginExpr())) {1732 Arg = CE->getArg(IndexOfSizeArg);1733 } else if (const CXXNewExpr *NE =1734 dyn_cast<CXXNewExpr>(Call.getOriginExpr())) {1735 if (NE->isArray()) {1736 Arg = *NE->getArraySize();1737 } else {1738 return State;1739 }1740 } else {1741 assert(false && "not a CallExpr or CXXNewExpr");1742 return nullptr;1743 }1744 1745 if (!RetVal)1746 RetVal = State->getSVal(Call.getOriginExpr(), C.getLocationContext());1747 1748 assert(Arg);1749 1750 auto DefArgVal =1751 State->getSVal(Arg, Call.getLocationContext()).getAs<DefinedSVal>();1752 1753 if (!DefArgVal)1754 return State;1755 1756 // Check if the allocation size is 0.1757 ProgramStateRef TrueState, FalseState;1758 SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();1759 DefinedSVal Zero =1760 SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();1761 1762 std::tie(TrueState, FalseState) =1763 State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));1764 1765 if (TrueState && !FalseState) {1766 SymbolRef Sym = RetVal->getAsLocSymbol();1767 if (!Sym)1768 return State;1769 1770 const RefState *RS = State->get<RegionState>(Sym);1771 if (RS) {1772 if (RS->isAllocated())1773 return TrueState->set<RegionState>(1774 Sym, RefState::getAllocatedOfSizeZero(RS));1775 return State;1776 }1777 // Case of zero-size realloc. Historically 'realloc(ptr, 0)' is treated as1778 // 'free(ptr)' and the returned value from 'realloc(ptr, 0)' is not1779 // tracked. Add zero-reallocated Sym to the state to catch references1780 // to zero-allocated memory.1781 return TrueState->add<ReallocSizeZeroSymbols>(Sym);1782 }1783 1784 // Assume the value is non-zero going forward.1785 assert(FalseState);1786 return FalseState;1787}1788 1789static QualType getDeepPointeeType(QualType T) {1790 QualType Result = T, PointeeType = T->getPointeeType();1791 while (!PointeeType.isNull()) {1792 Result = PointeeType;1793 PointeeType = PointeeType->getPointeeType();1794 }1795 return Result;1796}1797 1798/// \returns true if the constructor invoked by \p NE has an argument of a1799/// pointer/reference to a record type.1800static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) {1801 1802 const CXXConstructExpr *ConstructE = NE->getConstructExpr();1803 if (!ConstructE)1804 return false;1805 1806 if (!NE->getAllocatedType()->getAsCXXRecordDecl())1807 return false;1808 1809 const CXXConstructorDecl *CtorD = ConstructE->getConstructor();1810 1811 // Iterate over the constructor parameters.1812 for (const auto *CtorParam : CtorD->parameters()) {1813 1814 QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType();1815 if (CtorParamPointeeT.isNull())1816 continue;1817 1818 CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT);1819 1820 if (CtorParamPointeeT->getAsCXXRecordDecl())1821 return true;1822 }1823 1824 return false;1825}1826 1827ProgramStateRef1828MallocChecker::processNewAllocation(const CXXAllocatorCall &Call,1829 CheckerContext &C,1830 AllocationFamily Family) const {1831 if (!isStandardNewDelete(Call))1832 return nullptr;1833 1834 const CXXNewExpr *NE = Call.getOriginExpr();1835 const ParentMap &PM = C.getLocationContext()->getParentMap();1836 ProgramStateRef State = C.getState();1837 1838 // Non-trivial constructors have a chance to escape 'this', but marking all1839 // invocations of trivial constructors as escaped would cause too great of1840 // reduction of true positives, so let's just do that for constructors that1841 // have an argument of a pointer-to-record type.1842 if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE))1843 return State;1844 1845 // The return value from operator new is bound to a specified initialization1846 // value (if any) and we don't want to loose this value. So we call1847 // MallocUpdateRefState() instead of MallocMemAux() which breaks the1848 // existing binding.1849 SVal Target = Call.getObjectUnderConstruction();1850 if (Call.getOriginExpr()->isArray()) {1851 if (auto SizeEx = NE->getArraySize())1852 checkTaintedness(C, Call, C.getSVal(*SizeEx), State,1853 AllocationFamily(AF_CXXNewArray));1854 }1855 1856 State = MallocUpdateRefState(C, NE, State, Family, Target);1857 State = ProcessZeroAllocCheck(C, Call, 0, State, Target);1858 return State;1859}1860 1861void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call,1862 CheckerContext &C) const {1863 if (!C.wasInlined) {1864 ProgramStateRef State = processNewAllocation(1865 Call, C,1866 AllocationFamily(Call.getOriginExpr()->isArray() ? AF_CXXNewArray1867 : AF_CXXNew));1868 C.addTransition(State);1869 }1870}1871 1872static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {1873 // If the first selector piece is one of the names below, assume that the1874 // object takes ownership of the memory, promising to eventually deallocate it1875 // with free().1876 // Ex: [NSData dataWithBytesNoCopy:bytes length:10];1877 // (...unless a 'freeWhenDone' parameter is false, but that's checked later.)1878 StringRef FirstSlot = Call.getSelector().getNameForSlot(0);1879 return FirstSlot == "dataWithBytesNoCopy" ||1880 FirstSlot == "initWithBytesNoCopy" ||1881 FirstSlot == "initWithCharactersNoCopy";1882}1883 1884static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {1885 Selector S = Call.getSelector();1886 1887 // FIXME: We should not rely on fully-constrained symbols being folded.1888 for (unsigned i = 1; i < S.getNumArgs(); ++i)1889 if (S.getNameForSlot(i) == "freeWhenDone")1890 return !Call.getArgSVal(i).isZeroConstant();1891 1892 return std::nullopt;1893}1894 1895void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,1896 CheckerContext &C) const {1897 if (C.wasInlined)1898 return;1899 1900 if (!isKnownDeallocObjCMethodName(Call))1901 return;1902 1903 if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))1904 if (!*FreeWhenDone)1905 return;1906 1907 if (Call.hasNonZeroCallbackArg())1908 return;1909 1910 bool IsKnownToBeAllocatedMemory;1911 ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(),1912 /*Hold=*/true, IsKnownToBeAllocatedMemory,1913 AllocationFamily(AF_Malloc),1914 /*ReturnsNullOnFailure=*/true);1915 1916 C.addTransition(State);1917}1918 1919ProgramStateRef1920MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,1921 const OwnershipAttr *Att,1922 ProgramStateRef State) const {1923 if (!State)1924 return nullptr;1925 1926 auto attrClassName = Att->getModule()->getName();1927 auto Family = AllocationFamily(AF_Custom, attrClassName);1928 1929 if (!Att->args().empty()) {1930 return MallocMemAux(C, Call,1931 Call.getArgExpr(Att->args_begin()->getASTIndex()),1932 UnknownVal(), State, Family);1933 }1934 return MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, Family);1935}1936 1937ProgramStateRef MallocChecker::MallocBindRetVal(CheckerContext &C,1938 const CallEvent &Call,1939 ProgramStateRef State,1940 bool isAlloca) const {1941 const Expr *CE = Call.getOriginExpr();1942 1943 // We expect the allocation functions to return a pointer.1944 if (!Loc::isLocType(CE->getType()))1945 return nullptr;1946 1947 unsigned Count = C.blockCount();1948 SValBuilder &SVB = C.getSValBuilder();1949 const LocationContext *LCtx = C.getPredecessor()->getLocationContext();1950 DefinedSVal RetVal =1951 isAlloca ? SVB.getAllocaRegionVal(CE, LCtx, Count)1952 : SVB.getConjuredHeapSymbolVal(Call.getCFGElementRef(), LCtx,1953 CE->getType(), Count);1954 return State->BindExpr(CE, C.getLocationContext(), RetVal);1955}1956 1957ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,1958 const CallEvent &Call,1959 const Expr *SizeEx, SVal Init,1960 ProgramStateRef State,1961 AllocationFamily Family) const {1962 if (!State)1963 return nullptr;1964 1965 assert(SizeEx);1966 return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family);1967}1968 1969void MallocChecker::reportTaintBug(StringRef Msg, ProgramStateRef State,1970 CheckerContext &C,1971 llvm::ArrayRef<SymbolRef> TaintedSyms,1972 AllocationFamily Family) const {1973 if (ExplodedNode *N = C.generateNonFatalErrorNode(State, this)) {1974 auto R =1975 std::make_unique<PathSensitiveBugReport>(TaintedAllocChecker, Msg, N);1976 for (const auto *TaintedSym : TaintedSyms) {1977 R->markInteresting(TaintedSym);1978 }1979 C.emitReport(std::move(R));1980 }1981}1982 1983void MallocChecker::checkTaintedness(CheckerContext &C, const CallEvent &Call,1984 const SVal SizeSVal, ProgramStateRef State,1985 AllocationFamily Family) const {1986 if (!TaintedAllocChecker.isEnabled())1987 return;1988 std::vector<SymbolRef> TaintedSyms =1989 taint::getTaintedSymbols(State, SizeSVal);1990 if (TaintedSyms.empty())1991 return;1992 1993 SValBuilder &SVB = C.getSValBuilder();1994 QualType SizeTy = SVB.getContext().getSizeType();1995 QualType CmpTy = SVB.getConditionType();1996 // In case the symbol is tainted, we give a warning if the1997 // size is larger than SIZE_MAX/41998 BasicValueFactory &BVF = SVB.getBasicValueFactory();1999 const llvm::APSInt MaxValInt = BVF.getMaxValue(SizeTy);2000 NonLoc MaxLength =2001 SVB.makeIntVal(MaxValInt / APSIntType(MaxValInt).getValue(4));2002 std::optional<NonLoc> SizeNL = SizeSVal.getAs<NonLoc>();2003 auto Cmp = SVB.evalBinOpNN(State, BO_GE, *SizeNL, MaxLength, CmpTy)2004 .getAs<DefinedOrUnknownSVal>();2005 if (!Cmp)2006 return;2007 auto [StateTooLarge, StateNotTooLarge] = State->assume(*Cmp);2008 if (!StateTooLarge && StateNotTooLarge) {2009 // We can prove that size is not too large so there is no issue.2010 return;2011 }2012 2013 std::string Callee = "Memory allocation function";2014 if (Call.getCalleeIdentifier())2015 Callee = Call.getCalleeIdentifier()->getName().str();2016 reportTaintBug(2017 Callee + " is called with a tainted (potentially attacker controlled) "2018 "value. Make sure the value is bound checked.",2019 State, C, TaintedSyms, Family);2020}2021 2022ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,2023 const CallEvent &Call, SVal Size,2024 SVal Init, ProgramStateRef State,2025 AllocationFamily Family) const {2026 if (!State)2027 return nullptr;2028 2029 const Expr *CE = Call.getOriginExpr();2030 2031 // We expect the malloc functions to return a pointer.2032 // Should have been already checked.2033 assert(Loc::isLocType(CE->getType()) &&2034 "Allocation functions must return a pointer");2035 2036 const LocationContext *LCtx = C.getPredecessor()->getLocationContext();2037 SVal RetVal = State->getSVal(CE, C.getLocationContext());2038 2039 // Fill the region with the initialization value.2040 State = State->bindDefaultInitial(RetVal, Init, LCtx);2041 2042 // If Size is somehow undefined at this point, this line prevents a crash.2043 if (Size.isUndef())2044 Size = UnknownVal();2045 2046 checkTaintedness(C, Call, Size, State, AllocationFamily(AF_Malloc));2047 2048 // Set the region's extent.2049 State = setDynamicExtent(State, RetVal.getAsRegion(),2050 Size.castAs<DefinedOrUnknownSVal>());2051 2052 return MallocUpdateRefState(C, CE, State, Family);2053}2054 2055static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,2056 ProgramStateRef State,2057 AllocationFamily Family,2058 std::optional<SVal> RetVal) {2059 if (!State)2060 return nullptr;2061 2062 // Get the return value.2063 if (!RetVal)2064 RetVal = State->getSVal(E, C.getLocationContext());2065 2066 // We expect the malloc functions to return a pointer.2067 if (!RetVal->getAs<Loc>())2068 return nullptr;2069 2070 SymbolRef Sym = RetVal->getAsLocSymbol();2071 2072 // NOTE: If this was an `alloca()` call, then `RetVal` holds an2073 // `AllocaRegion`, so `Sym` will be a nullpointer because `AllocaRegion`s do2074 // not have an associated symbol. However, this distinct region type means2075 // that we don't need to store anything about them in `RegionState`.2076 2077 if (Sym)2078 return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));2079 2080 return State;2081}2082 2083ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,2084 const CallEvent &Call,2085 const OwnershipAttr *Att,2086 ProgramStateRef State) const {2087 if (!State)2088 return nullptr;2089 2090 auto attrClassName = Att->getModule()->getName();2091 auto Family = AllocationFamily(AF_Custom, attrClassName);2092 2093 bool IsKnownToBeAllocated = false;2094 2095 for (const auto &Arg : Att->args()) {2096 ProgramStateRef StateI =2097 FreeMemAux(C, Call, State, Arg.getASTIndex(),2098 Att->getOwnKind() == OwnershipAttr::Holds,2099 IsKnownToBeAllocated, Family);2100 if (StateI)2101 State = StateI;2102 }2103 return State;2104}2105 2106ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,2107 const CallEvent &Call,2108 ProgramStateRef State, unsigned Num,2109 bool Hold, bool &IsKnownToBeAllocated,2110 AllocationFamily Family,2111 bool ReturnsNullOnFailure) const {2112 if (!State)2113 return nullptr;2114 2115 if (Call.getNumArgs() < (Num + 1))2116 return nullptr;2117 2118 return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold,2119 IsKnownToBeAllocated, Family, ReturnsNullOnFailure);2120}2121 2122/// Checks if the previous call to free on the given symbol failed - if free2123/// failed, returns true. Also, returns the corresponding return value symbol.2124static bool didPreviousFreeFail(ProgramStateRef State,2125 SymbolRef Sym, SymbolRef &RetStatusSymbol) {2126 const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);2127 if (Ret) {2128 assert(*Ret && "We should not store the null return symbol");2129 ConstraintManager &CMgr = State->getConstraintManager();2130 ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret);2131 RetStatusSymbol = *Ret;2132 return FreeFailed.isConstrainedTrue();2133 }2134 return false;2135}2136 2137static void printOwnershipTakesList(raw_ostream &os, CheckerContext &C,2138 const Expr *E) {2139 const CallExpr *CE = dyn_cast<CallExpr>(E);2140 2141 if (!CE)2142 return;2143 2144 const FunctionDecl *FD = CE->getDirectCallee();2145 if (!FD)2146 return;2147 2148 // Only one ownership_takes attribute is allowed.2149 for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {2150 if (I->getOwnKind() != OwnershipAttr::Takes)2151 continue;2152 2153 os << ", which takes ownership of '" << I->getModule()->getName() << '\'';2154 break;2155 }2156}2157 2158static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {2159 if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {2160 // FIXME: This doesn't handle indirect calls.2161 const FunctionDecl *FD = CE->getDirectCallee();2162 if (!FD)2163 return false;2164 2165 os << '\'' << *FD;2166 2167 if (!FD->isOverloadedOperator())2168 os << "()";2169 2170 os << '\'';2171 return true;2172 }2173 2174 if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {2175 if (Msg->isInstanceMessage())2176 os << "-";2177 else2178 os << "+";2179 Msg->getSelector().print(os);2180 return true;2181 }2182 2183 if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {2184 os << "'"2185 << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())2186 << "'";2187 return true;2188 }2189 2190 if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {2191 os << "'"2192 << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())2193 << "'";2194 return true;2195 }2196 2197 return false;2198}2199 2200static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {2201 2202 switch (Family.Kind) {2203 case AF_Malloc:2204 os << "'malloc()'";2205 return;2206 case AF_CXXNew:2207 os << "'new'";2208 return;2209 case AF_CXXNewArray:2210 os << "'new[]'";2211 return;2212 case AF_IfNameIndex:2213 os << "'if_nameindex()'";2214 return;2215 case AF_InnerBuffer:2216 os << "container-specific allocator";2217 return;2218 case AF_Custom:2219 os << Family.CustomName.value();2220 return;2221 case AF_Alloca:2222 case AF_None:2223 assert(false && "not a deallocation expression");2224 }2225}2226 2227static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {2228 switch (Family.Kind) {2229 case AF_Malloc:2230 os << "'free()'";2231 return;2232 case AF_CXXNew:2233 os << "'delete'";2234 return;2235 case AF_CXXNewArray:2236 os << "'delete[]'";2237 return;2238 case AF_IfNameIndex:2239 os << "'if_freenameindex()'";2240 return;2241 case AF_InnerBuffer:2242 os << "container-specific deallocator";2243 return;2244 case AF_Custom:2245 os << "function that takes ownership of '" << Family.CustomName.value()2246 << "\'";2247 return;2248 case AF_Alloca:2249 case AF_None:2250 assert(false && "not a deallocation expression");2251 }2252}2253 2254ProgramStateRef2255MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,2256 const CallEvent &Call, ProgramStateRef State,2257 bool Hold, bool &IsKnownToBeAllocated,2258 AllocationFamily Family, bool ReturnsNullOnFailure,2259 std::optional<SVal> ArgValOpt) const {2260 2261 if (!State)2262 return nullptr;2263 2264 SVal ArgVal = ArgValOpt.value_or(C.getSVal(ArgExpr));2265 if (!isa<DefinedOrUnknownSVal>(ArgVal))2266 return nullptr;2267 DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();2268 2269 // Check for null dereferences.2270 if (!isa<Loc>(location))2271 return nullptr;2272 2273 // The explicit NULL case, no operation is performed.2274 ProgramStateRef notNullState, nullState;2275 std::tie(notNullState, nullState) = State->assume(location);2276 if (nullState && !notNullState)2277 return nullptr;2278 2279 // Unknown values could easily be okay2280 // Undefined values are handled elsewhere2281 if (ArgVal.isUnknownOrUndef())2282 return nullptr;2283 2284 const MemRegion *R = ArgVal.getAsRegion();2285 const Expr *ParentExpr = Call.getOriginExpr();2286 2287 // NOTE: We detected a bug, but the checker under whose name we would emit the2288 // error could be disabled. Generally speaking, the MallocChecker family is an2289 // integral part of the Static Analyzer, and disabling any part of it should2290 // only be done under exceptional circumstances, such as frequent false2291 // positives. If this is the case, we can reasonably believe that there are2292 // serious faults in our understanding of the source code, and even if we2293 // don't emit an warning, we should terminate further analysis with a sink2294 // node.2295 2296 // Nonlocs can't be freed, of course.2297 // Non-region locations (labels and fixed addresses) also shouldn't be freed.2298 if (!R) {2299 // Exception:2300 // If the macro ZERO_SIZE_PTR is defined, this could be a kernel source2301 // code. In that case, the ZERO_SIZE_PTR defines a special value used for a2302 // zero-sized memory block which is allowed to be freed, despite not being a2303 // null pointer.2304 if (Family.Kind != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal))2305 HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,2306 Family);2307 return nullptr;2308 }2309 2310 R = R->StripCasts();2311 2312 // Blocks might show up as heap data, but should not be free()d2313 if (isa<BlockDataRegion>(R)) {2314 HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,2315 Family);2316 return nullptr;2317 }2318 2319 // Parameters, locals, statics, globals, and memory returned by2320 // __builtin_alloca() shouldn't be freed.2321 if (!R->hasMemorySpace<UnknownSpaceRegion, HeapSpaceRegion>(State)) {2322 // Regions returned by malloc() are represented by SymbolicRegion objects2323 // within HeapSpaceRegion. Of course, free() can work on memory allocated2324 // outside the current function, so UnknownSpaceRegion is also a2325 // possibility here.2326 2327 if (isa<AllocaRegion>(R))2328 HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());2329 else2330 HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,2331 Family);2332 2333 return nullptr;2334 }2335 2336 const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion());2337 // Various cases could lead to non-symbol values here.2338 // For now, ignore them.2339 if (!SrBase)2340 return nullptr;2341 2342 SymbolRef SymBase = SrBase->getSymbol();2343 const RefState *RsBase = State->get<RegionState>(SymBase);2344 SymbolRef PreviousRetStatusSymbol = nullptr;2345 2346 IsKnownToBeAllocated =2347 RsBase && (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero());2348 2349 if (RsBase) {2350 2351 // Memory returned by alloca() shouldn't be freed.2352 if (RsBase->getAllocationFamily().Kind == AF_Alloca) {2353 HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());2354 return nullptr;2355 }2356 2357 // Check for double free first.2358 if ((RsBase->isReleased() || RsBase->isRelinquished()) &&2359 !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {2360 HandleDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),2361 SymBase, PreviousRetStatusSymbol);2362 return nullptr;2363 }2364 2365 // If the pointer is allocated or escaped, but we are now trying to free it,2366 // check that the call to free is proper.2367 if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||2368 RsBase->isEscaped()) {2369 2370 // Check if an expected deallocation function matches the real one.2371 bool DeallocMatchesAlloc = RsBase->getAllocationFamily() == Family;2372 if (!DeallocMatchesAlloc) {2373 HandleMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr,2374 RsBase, SymBase, Hold);2375 return nullptr;2376 }2377 2378 // Check if the memory location being freed is the actual location2379 // allocated, or an offset.2380 RegionOffset Offset = R->getAsOffset();2381 if (Offset.isValid() &&2382 !Offset.hasSymbolicOffset() &&2383 Offset.getOffset() != 0) {2384 const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());2385 HandleOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,2386 Family, AllocExpr);2387 return nullptr;2388 }2389 }2390 }2391 2392 if (SymBase->getType()->isFunctionPointerType()) {2393 HandleFunctionPtrFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,2394 Family);2395 return nullptr;2396 }2397 2398 // Clean out the info on previous call to free return info.2399 State = State->remove<FreeReturnValue>(SymBase);2400 2401 // Keep track of the return value. If it is NULL, we will know that free2402 // failed.2403 if (ReturnsNullOnFailure) {2404 SVal RetVal = C.getSVal(ParentExpr);2405 SymbolRef RetStatusSymbol = RetVal.getAsSymbol();2406 if (RetStatusSymbol) {2407 C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol);2408 State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol);2409 }2410 }2411 2412 // If we don't know anything about this symbol, a free on it may be totally2413 // valid. If this is the case, lets assume that the allocation family of the2414 // freeing function is the same as the symbols allocation family, and go with2415 // that.2416 assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family));2417 2418 // Assume that after memory is freed, it contains unknown values. This2419 // conforts languages standards, since reading from freed memory is considered2420 // UB and may result in arbitrary value.2421 State = State->invalidateRegions({location}, Call.getCFGElementRef(),2422 C.blockCount(), C.getLocationContext(),2423 /*CausesPointerEscape=*/false,2424 /*InvalidatedSymbols=*/nullptr);2425 2426 // Normal free.2427 if (Hold)2428 return State->set<RegionState>(SymBase,2429 RefState::getRelinquished(Family,2430 ParentExpr));2431 2432 return State->set<RegionState>(SymBase,2433 RefState::getReleased(Family, ParentExpr));2434}2435 2436template <class T>2437const T *MallocChecker::getRelevantFrontendAs(AllocationFamily Family) const {2438 switch (Family.Kind) {2439 case AF_Malloc:2440 case AF_Alloca:2441 case AF_Custom:2442 case AF_IfNameIndex:2443 return MallocChecker.getAs<T>();2444 case AF_CXXNew:2445 case AF_CXXNewArray: {2446 const T *ND = NewDeleteChecker.getAs<T>();2447 const T *NDL = NewDeleteLeaksChecker.getAs<T>();2448 // Bugs corresponding to C++ new/delete allocations are split between these2449 // two frontends.2450 if constexpr (std::is_same_v<T, CheckerFrontend>) {2451 assert(ND && NDL && "Casting to CheckerFrontend always succeeds");2452 // Prefer NewDelete unless it's disabled and NewDeleteLeaks is enabled.2453 return (!ND->isEnabled() && NDL->isEnabled()) ? NDL : ND;2454 }2455 assert(!(ND && NDL) &&2456 "NewDelete and NewDeleteLeaks must not share a bug type");2457 return ND ? ND : NDL;2458 }2459 case AF_InnerBuffer:2460 return InnerPointerChecker.getAs<T>();2461 case AF_None:2462 assert(false && "no family");2463 return nullptr;2464 }2465 assert(false && "unhandled family");2466 return nullptr;2467}2468template <class T>2469const T *MallocChecker::getRelevantFrontendAs(CheckerContext &C,2470 SymbolRef Sym) const {2471 if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym))2472 return MallocChecker.getAs<T>();2473 2474 const RefState *RS = C.getState()->get<RegionState>(Sym);2475 assert(RS);2476 return getRelevantFrontendAs<T>(RS->getAllocationFamily());2477}2478 2479bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {2480 if (std::optional<nonloc::ConcreteInt> IntVal =2481 V.getAs<nonloc::ConcreteInt>())2482 os << "an integer (" << IntVal->getValue() << ")";2483 else if (std::optional<loc::ConcreteInt> ConstAddr =2484 V.getAs<loc::ConcreteInt>())2485 os << "a constant address (" << ConstAddr->getValue() << ")";2486 else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())2487 os << "the address of the label '" << Label->getLabel()->getName() << "'";2488 else2489 return false;2490 2491 return true;2492}2493 2494bool MallocChecker::SummarizeRegion(ProgramStateRef State, raw_ostream &os,2495 const MemRegion *MR) {2496 switch (MR->getKind()) {2497 case MemRegion::FunctionCodeRegionKind: {2498 const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl();2499 if (FD)2500 os << "the address of the function '" << *FD << '\'';2501 else2502 os << "the address of a function";2503 return true;2504 }2505 case MemRegion::BlockCodeRegionKind:2506 os << "block text";2507 return true;2508 case MemRegion::BlockDataRegionKind:2509 // FIXME: where the block came from?2510 os << "a block";2511 return true;2512 default: {2513 const MemSpaceRegion *MS = MR->getMemorySpace(State);2514 2515 if (isa<StackLocalsSpaceRegion>(MS)) {2516 const VarRegion *VR = dyn_cast<VarRegion>(MR);2517 const VarDecl *VD;2518 if (VR)2519 VD = VR->getDecl();2520 else2521 VD = nullptr;2522 2523 if (VD)2524 os << "the address of the local variable '" << VD->getName() << "'";2525 else2526 os << "the address of a local stack variable";2527 return true;2528 }2529 2530 if (isa<StackArgumentsSpaceRegion>(MS)) {2531 const VarRegion *VR = dyn_cast<VarRegion>(MR);2532 const VarDecl *VD;2533 if (VR)2534 VD = VR->getDecl();2535 else2536 VD = nullptr;2537 2538 if (VD)2539 os << "the address of the parameter '" << VD->getName() << "'";2540 else2541 os << "the address of a parameter";2542 return true;2543 }2544 2545 if (isa<GlobalsSpaceRegion>(MS)) {2546 const VarRegion *VR = dyn_cast<VarRegion>(MR);2547 const VarDecl *VD;2548 if (VR)2549 VD = VR->getDecl();2550 else2551 VD = nullptr;2552 2553 if (VD) {2554 if (VD->isStaticLocal())2555 os << "the address of the static variable '" << VD->getName() << "'";2556 else2557 os << "the address of the global variable '" << VD->getName() << "'";2558 } else2559 os << "the address of a global variable";2560 return true;2561 }2562 2563 return false;2564 }2565 }2566}2567 2568void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,2569 SourceRange Range,2570 const Expr *DeallocExpr,2571 AllocationFamily Family) const {2572 const BadFree *Frontend = getRelevantFrontendAs<BadFree>(Family);2573 if (!Frontend)2574 return;2575 if (!Frontend->isEnabled()) {2576 C.addSink();2577 return;2578 }2579 2580 if (ExplodedNode *N = C.generateErrorNode()) {2581 SmallString<100> buf;2582 llvm::raw_svector_ostream os(buf);2583 2584 const MemRegion *MR = ArgVal.getAsRegion();2585 while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))2586 MR = ER->getSuperRegion();2587 2588 os << "Argument to ";2589 if (!printMemFnName(os, C, DeallocExpr))2590 os << "deallocator";2591 2592 os << " is ";2593 bool Summarized =2594 MR ? SummarizeRegion(C.getState(), os, MR) : SummarizeValue(os, ArgVal);2595 if (Summarized)2596 os << ", which is not memory allocated by ";2597 else2598 os << "not memory allocated by ";2599 2600 printExpectedAllocName(os, Family);2601 2602 auto R = std::make_unique<PathSensitiveBugReport>(Frontend->BadFreeBug,2603 os.str(), N);2604 R->markInteresting(MR);2605 R->addRange(Range);2606 C.emitReport(std::move(R));2607 }2608}2609 2610void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal,2611 SourceRange Range) const {2612 const FreeAlloca *Frontend;2613 2614 if (MallocChecker.isEnabled())2615 Frontend = &MallocChecker;2616 else if (MismatchedDeallocatorChecker.isEnabled())2617 Frontend = &MismatchedDeallocatorChecker;2618 else {2619 C.addSink();2620 return;2621 }2622 2623 if (ExplodedNode *N = C.generateErrorNode()) {2624 auto R = std::make_unique<PathSensitiveBugReport>(2625 Frontend->FreeAllocaBug,2626 "Memory allocated by 'alloca()' should not be deallocated", N);2627 R->markInteresting(ArgVal.getAsRegion());2628 R->addRange(Range);2629 C.emitReport(std::move(R));2630 }2631}2632 2633void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,2634 SourceRange Range,2635 const Expr *DeallocExpr,2636 const RefState *RS, SymbolRef Sym,2637 bool OwnershipTransferred) const {2638 if (!MismatchedDeallocatorChecker.isEnabled()) {2639 C.addSink();2640 return;2641 }2642 2643 if (ExplodedNode *N = C.generateErrorNode()) {2644 SmallString<100> buf;2645 llvm::raw_svector_ostream os(buf);2646 2647 const Expr *AllocExpr = cast<Expr>(RS->getStmt());2648 SmallString<20> AllocBuf;2649 llvm::raw_svector_ostream AllocOs(AllocBuf);2650 SmallString<20> DeallocBuf;2651 llvm::raw_svector_ostream DeallocOs(DeallocBuf);2652 2653 if (OwnershipTransferred) {2654 if (printMemFnName(DeallocOs, C, DeallocExpr))2655 os << DeallocOs.str() << " cannot";2656 else2657 os << "Cannot";2658 2659 os << " take ownership of memory";2660 2661 if (printMemFnName(AllocOs, C, AllocExpr))2662 os << " allocated by " << AllocOs.str();2663 } else {2664 os << "Memory";2665 if (printMemFnName(AllocOs, C, AllocExpr))2666 os << " allocated by " << AllocOs.str();2667 2668 os << " should be deallocated by ";2669 printExpectedDeallocName(os, RS->getAllocationFamily());2670 2671 if (printMemFnName(DeallocOs, C, DeallocExpr))2672 os << ", not " << DeallocOs.str();2673 2674 printOwnershipTakesList(os, C, DeallocExpr);2675 }2676 2677 auto R = std::make_unique<PathSensitiveBugReport>(2678 MismatchedDeallocatorChecker.MismatchedDeallocBug, os.str(), N);2679 R->markInteresting(Sym);2680 R->addRange(Range);2681 R->addVisitor<MallocBugVisitor>(Sym);2682 C.emitReport(std::move(R));2683 }2684}2685 2686void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,2687 SourceRange Range, const Expr *DeallocExpr,2688 AllocationFamily Family,2689 const Expr *AllocExpr) const {2690 const OffsetFree *Frontend = getRelevantFrontendAs<OffsetFree>(Family);2691 if (!Frontend)2692 return;2693 if (!Frontend->isEnabled()) {2694 C.addSink();2695 return;2696 }2697 2698 ExplodedNode *N = C.generateErrorNode();2699 if (!N)2700 return;2701 2702 SmallString<100> buf;2703 llvm::raw_svector_ostream os(buf);2704 SmallString<20> AllocNameBuf;2705 llvm::raw_svector_ostream AllocNameOs(AllocNameBuf);2706 2707 const MemRegion *MR = ArgVal.getAsRegion();2708 assert(MR && "Only MemRegion based symbols can have offset free errors");2709 2710 RegionOffset Offset = MR->getAsOffset();2711 assert((Offset.isValid() &&2712 !Offset.hasSymbolicOffset() &&2713 Offset.getOffset() != 0) &&2714 "Only symbols with a valid offset can have offset free errors");2715 2716 int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();2717 2718 os << "Argument to ";2719 if (!printMemFnName(os, C, DeallocExpr))2720 os << "deallocator";2721 os << " is offset by "2722 << offsetBytes2723 << " "2724 << ((abs(offsetBytes) > 1) ? "bytes" : "byte")2725 << " from the start of ";2726 if (AllocExpr && printMemFnName(AllocNameOs, C, AllocExpr))2727 os << "memory allocated by " << AllocNameOs.str();2728 else2729 os << "allocated memory";2730 2731 auto R = std::make_unique<PathSensitiveBugReport>(Frontend->OffsetFreeBug,2732 os.str(), N);2733 R->markInteresting(MR->getBaseRegion());2734 R->addRange(Range);2735 C.emitReport(std::move(R));2736}2737 2738void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,2739 SymbolRef Sym) const {2740 const UseFree *Frontend = getRelevantFrontendAs<UseFree>(C, Sym);2741 if (!Frontend)2742 return;2743 if (!Frontend->isEnabled()) {2744 C.addSink();2745 return;2746 }2747 2748 if (ExplodedNode *N = C.generateErrorNode()) {2749 AllocationFamily AF =2750 C.getState()->get<RegionState>(Sym)->getAllocationFamily();2751 2752 auto R = std::make_unique<PathSensitiveBugReport>(2753 Frontend->UseFreeBug,2754 AF.Kind == AF_InnerBuffer2755 ? "Inner pointer of container used after re/deallocation"2756 : "Use of memory after it is released",2757 N);2758 2759 R->markInteresting(Sym);2760 R->addRange(Range);2761 R->addVisitor<MallocBugVisitor>(Sym);2762 2763 if (AF.Kind == AF_InnerBuffer)2764 R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));2765 2766 C.emitReport(std::move(R));2767 }2768}2769 2770void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,2771 bool Released, SymbolRef Sym,2772 SymbolRef PrevSym) const {2773 const DoubleFree *Frontend = getRelevantFrontendAs<DoubleFree>(C, Sym);2774 if (!Frontend)2775 return;2776 if (!Frontend->isEnabled()) {2777 C.addSink();2778 return;2779 }2780 2781 if (ExplodedNode *N = C.generateErrorNode()) {2782 auto R = std::make_unique<PathSensitiveBugReport>(2783 Frontend->DoubleFreeBug,2784 (Released ? "Attempt to release already released memory"2785 : "Attempt to release non-owned memory"),2786 N);2787 if (Range.isValid())2788 R->addRange(Range);2789 R->markInteresting(Sym);2790 if (PrevSym)2791 R->markInteresting(PrevSym);2792 R->addVisitor<MallocBugVisitor>(Sym);2793 C.emitReport(std::move(R));2794 }2795}2796 2797void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,2798 SymbolRef Sym) const {2799 const UseZeroAllocated *Frontend =2800 getRelevantFrontendAs<UseZeroAllocated>(C, Sym);2801 if (!Frontend)2802 return;2803 if (!Frontend->isEnabled()) {2804 C.addSink();2805 return;2806 }2807 2808 if (ExplodedNode *N = C.generateErrorNode()) {2809 auto R = std::make_unique<PathSensitiveBugReport>(2810 Frontend->UseZeroAllocatedBug, "Use of memory allocated with size zero",2811 N);2812 2813 R->addRange(Range);2814 if (Sym) {2815 R->markInteresting(Sym);2816 R->addVisitor<MallocBugVisitor>(Sym);2817 }2818 C.emitReport(std::move(R));2819 }2820}2821 2822void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal,2823 SourceRange Range,2824 const Expr *FreeExpr,2825 AllocationFamily Family) const {2826 const BadFree *Frontend = getRelevantFrontendAs<BadFree>(Family);2827 if (!Frontend)2828 return;2829 if (!Frontend->isEnabled()) {2830 C.addSink();2831 return;2832 }2833 2834 if (ExplodedNode *N = C.generateErrorNode()) {2835 SmallString<100> Buf;2836 llvm::raw_svector_ostream Os(Buf);2837 2838 const MemRegion *MR = ArgVal.getAsRegion();2839 while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))2840 MR = ER->getSuperRegion();2841 2842 Os << "Argument to ";2843 if (!printMemFnName(Os, C, FreeExpr))2844 Os << "deallocator";2845 2846 Os << " is a function pointer";2847 2848 auto R = std::make_unique<PathSensitiveBugReport>(Frontend->BadFreeBug,2849 Os.str(), N);2850 R->markInteresting(MR);2851 R->addRange(Range);2852 C.emitReport(std::move(R));2853 }2854}2855 2856ProgramStateRef2857MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,2858 bool ShouldFreeOnFail, ProgramStateRef State,2859 AllocationFamily Family, bool SuffixWithN) const {2860 if (!State)2861 return nullptr;2862 2863 const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());2864 2865 if ((SuffixWithN && CE->getNumArgs() < 3) || CE->getNumArgs() < 2)2866 return nullptr;2867 2868 const Expr *arg0Expr = CE->getArg(0);2869 SVal Arg0Val = C.getSVal(arg0Expr);2870 if (!isa<DefinedOrUnknownSVal>(Arg0Val))2871 return nullptr;2872 DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();2873 2874 SValBuilder &svalBuilder = C.getSValBuilder();2875 2876 DefinedOrUnknownSVal PtrEQ = svalBuilder.evalEQ(2877 State, arg0Val, svalBuilder.makeNullWithType(arg0Expr->getType()));2878 2879 // Get the size argument.2880 const Expr *Arg1 = CE->getArg(1);2881 2882 // Get the value of the size argument.2883 SVal TotalSize = C.getSVal(Arg1);2884 if (SuffixWithN)2885 TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));2886 if (!isa<DefinedOrUnknownSVal>(TotalSize))2887 return nullptr;2888 2889 // Compare the size argument to 0.2890 DefinedOrUnknownSVal SizeZero = svalBuilder.evalEQ(2891 State, TotalSize.castAs<DefinedOrUnknownSVal>(),2892 svalBuilder.makeIntValWithWidth(2893 svalBuilder.getContext().getCanonicalSizeType(), 0));2894 2895 ProgramStateRef StatePtrIsNull, StatePtrNotNull;2896 std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ);2897 ProgramStateRef StateSizeIsZero, StateSizeNotZero;2898 std::tie(StateSizeIsZero, StateSizeNotZero) = State->assume(SizeZero);2899 // We only assume exceptional states if they are definitely true; if the2900 // state is under-constrained, assume regular realloc behavior.2901 bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;2902 bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;2903 2904 // If the ptr is NULL and the size is not 0, the call is equivalent to2905 // malloc(size).2906 if (PrtIsNull && !SizeIsZero) {2907 ProgramStateRef stateMalloc = MallocMemAux(2908 C, Call, TotalSize, UndefinedVal(), StatePtrIsNull, Family);2909 return stateMalloc;2910 }2911 2912 // Proccess as allocation of 0 bytes.2913 if (PrtIsNull && SizeIsZero)2914 return State;2915 2916 assert(!PrtIsNull);2917 2918 bool IsKnownToBeAllocated = false;2919 2920 // If the size is 0, free the memory.2921 if (SizeIsZero)2922 // The semantics of the return value are:2923 // If size was equal to 0, either NULL or a pointer suitable to be passed2924 // to free() is returned. We just free the input pointer and do not add2925 // any constrains on the output pointer.2926 if (ProgramStateRef stateFree = FreeMemAux(2927 C, Call, StateSizeIsZero, 0, false, IsKnownToBeAllocated, Family))2928 return stateFree;2929 2930 // Default behavior.2931 if (ProgramStateRef stateFree =2932 FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocated, Family)) {2933 2934 ProgramStateRef stateRealloc =2935 MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Family);2936 if (!stateRealloc)2937 return nullptr;2938 2939 OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure;2940 if (ShouldFreeOnFail)2941 Kind = OAR_FreeOnFailure;2942 else if (!IsKnownToBeAllocated)2943 Kind = OAR_DoNotTrackAfterFailure;2944 2945 // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).2946 SymbolRef FromPtr = arg0Val.getLocSymbolInBase();2947 SVal RetVal = stateRealloc->getSVal(CE, C.getLocationContext());2948 SymbolRef ToPtr = RetVal.getAsSymbol();2949 assert(FromPtr && ToPtr &&2950 "By this point, FreeMemAux and MallocMemAux should have checked "2951 "whether the argument or the return value is symbolic!");2952 2953 // Record the info about the reallocated symbol so that we could properly2954 // process failed reallocation.2955 stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,2956 ReallocPair(FromPtr, Kind));2957 // The reallocated symbol should stay alive for as long as the new symbol.2958 C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);2959 return stateRealloc;2960 }2961 return nullptr;2962}2963 2964ProgramStateRef MallocChecker::CallocMem(CheckerContext &C,2965 const CallEvent &Call,2966 ProgramStateRef State) const {2967 if (!State)2968 return nullptr;2969 2970 if (Call.getNumArgs() < 2)2971 return nullptr;2972 2973 SValBuilder &svalBuilder = C.getSValBuilder();2974 SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);2975 SVal TotalSize =2976 evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));2977 2978 return MallocMemAux(C, Call, TotalSize, zeroVal, State,2979 AllocationFamily(AF_Malloc));2980}2981 2982MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,2983 SymbolRef Sym,2984 CheckerContext &C) {2985 const LocationContext *LeakContext = N->getLocationContext();2986 // Walk the ExplodedGraph backwards and find the first node that referred to2987 // the tracked symbol.2988 const ExplodedNode *AllocNode = N;2989 const MemRegion *ReferenceRegion = nullptr;2990 2991 while (N) {2992 ProgramStateRef State = N->getState();2993 if (!State->get<RegionState>(Sym))2994 break;2995 2996 // Find the most recent expression bound to the symbol in the current2997 // context.2998 if (!ReferenceRegion) {2999 if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {3000 SVal Val = State->getSVal(MR);3001 if (Val.getAsLocSymbol() == Sym) {3002 const VarRegion *VR = MR->getBaseRegion()->getAs<VarRegion>();3003 // Do not show local variables belonging to a function other than3004 // where the error is reported.3005 if (!VR || (VR->getStackFrame() == LeakContext->getStackFrame()))3006 ReferenceRegion = MR;3007 }3008 }3009 }3010 3011 // Allocation node, is the last node in the current or parent context in3012 // which the symbol was tracked.3013 const LocationContext *NContext = N->getLocationContext();3014 if (NContext == LeakContext ||3015 NContext->isParentOf(LeakContext))3016 AllocNode = N;3017 N = N->pred_empty() ? nullptr : *(N->pred_begin());3018 }3019 3020 return LeakInfo(AllocNode, ReferenceRegion);3021}3022 3023void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,3024 CheckerContext &C) const {3025 assert(N && "HandleLeak is only called with a non-null node");3026 3027 const RefState *RS = C.getState()->get<RegionState>(Sym);3028 assert(RS && "cannot leak an untracked symbol");3029 AllocationFamily Family = RS->getAllocationFamily();3030 3031 if (Family.Kind == AF_Alloca)3032 return;3033 3034 const Leak *Frontend = getRelevantFrontendAs<Leak>(Family);3035 // Note that for leaks we don't add a sink when the relevant frontend is3036 // disabled because the leak is reported with a non-fatal error node, while3037 // the sink would be the "silent" alternative of a (fatal) error node.3038 if (!Frontend || !Frontend->isEnabled())3039 return;3040 3041 // Most bug reports are cached at the location where they occurred.3042 // With leaks, we want to unique them by the location where they were3043 // allocated, and only report a single path.3044 PathDiagnosticLocation LocUsedForUniqueing;3045 const ExplodedNode *AllocNode = nullptr;3046 const MemRegion *Region = nullptr;3047 std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);3048 3049 const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics();3050 if (AllocationStmt)3051 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,3052 C.getSourceManager(),3053 AllocNode->getLocationContext());3054 3055 SmallString<200> buf;3056 llvm::raw_svector_ostream os(buf);3057 if (Region && Region->canPrintPretty()) {3058 os << "Potential leak of memory pointed to by ";3059 Region->printPretty(os);3060 } else {3061 os << "Potential memory leak";3062 }3063 3064 auto R = std::make_unique<PathSensitiveBugReport>(3065 Frontend->LeakBug, os.str(), N, LocUsedForUniqueing,3066 AllocNode->getLocationContext()->getDecl());3067 R->markInteresting(Sym);3068 R->addVisitor<MallocBugVisitor>(Sym, true);3069 if (ShouldRegisterNoOwnershipChangeVisitor)3070 R->addVisitor<NoMemOwnershipChangeVisitor>(Sym, this);3071 C.emitReport(std::move(R));3072}3073 3074void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,3075 CheckerContext &C) const3076{3077 ProgramStateRef state = C.getState();3078 RegionStateTy OldRS = state->get<RegionState>();3079 RegionStateTy::Factory &F = state->get_context<RegionState>();3080 3081 RegionStateTy RS = OldRS;3082 SmallVector<SymbolRef, 2> Errors;3083 for (auto [Sym, State] : RS) {3084 if (SymReaper.isDead(Sym)) {3085 if (State.isAllocated() || State.isAllocatedOfSizeZero())3086 Errors.push_back(Sym);3087 // Remove the dead symbol from the map.3088 RS = F.remove(RS, Sym);3089 }3090 }3091 3092 if (RS == OldRS) {3093 // We shouldn't have touched other maps yet.3094 assert(state->get<ReallocPairs>() ==3095 C.getState()->get<ReallocPairs>());3096 assert(state->get<FreeReturnValue>() ==3097 C.getState()->get<FreeReturnValue>());3098 return;3099 }3100 3101 // Cleanup the Realloc Pairs Map.3102 ReallocPairsTy RP = state->get<ReallocPairs>();3103 for (auto [Sym, ReallocPair] : RP) {3104 if (SymReaper.isDead(Sym) || SymReaper.isDead(ReallocPair.ReallocatedSym)) {3105 state = state->remove<ReallocPairs>(Sym);3106 }3107 }3108 3109 // Cleanup the FreeReturnValue Map.3110 FreeReturnValueTy FR = state->get<FreeReturnValue>();3111 for (auto [Sym, RetSym] : FR) {3112 if (SymReaper.isDead(Sym) || SymReaper.isDead(RetSym)) {3113 state = state->remove<FreeReturnValue>(Sym);3114 }3115 }3116 3117 // Generate leak node.3118 ExplodedNode *N = C.getPredecessor();3119 if (!Errors.empty()) {3120 N = C.generateNonFatalErrorNode(C.getState());3121 if (N) {3122 for (SymbolRef Sym : Errors) {3123 HandleLeak(Sym, N, C);3124 }3125 }3126 }3127 3128 C.addTransition(state->set<RegionState>(RS), N);3129}3130 3131// Allowlist of owning smart pointers we want to recognize.3132// Start with unique_ptr and shared_ptr; weak_ptr is excluded intentionally3133// because it does not own the pointee.3134static bool isSmartPtrName(StringRef Name) {3135 return Name == "unique_ptr" || Name == "shared_ptr";3136}3137 3138// Check if a type is a smart owning pointer type.3139static bool isSmartPtrType(QualType QT) {3140 QT = QT->getCanonicalTypeUnqualified();3141 3142 if (const auto *TST = QT->getAs<TemplateSpecializationType>()) {3143 const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();3144 if (!TD)3145 return false;3146 3147 const auto *ND = dyn_cast_or_null<NamedDecl>(TD->getTemplatedDecl());3148 if (!ND)3149 return false;3150 3151 // For broader coverage we recognize all template classes with names that3152 // match the allowlist even if they are not declared in namespace 'std'.3153 return isSmartPtrName(ND->getName());3154 }3155 3156 return false;3157}3158 3159/// Helper struct for collecting smart owning pointer field regions.3160/// This allows both hasSmartPtrField and3161/// collectSmartPtrFieldRegions to share the same traversal logic,3162/// ensuring consistency.3163struct FieldConsumer {3164 const MemRegion *Reg;3165 CheckerContext *C;3166 llvm::SmallPtrSetImpl<const MemRegion *> *Out;3167 3168 FieldConsumer(const MemRegion *Reg, CheckerContext &C,3169 llvm::SmallPtrSetImpl<const MemRegion *> &Out)3170 : Reg(Reg), C(&C), Out(&Out) {}3171 3172 void consume(const FieldDecl *FD) {3173 SVal L = C->getState()->getLValue(FD, loc::MemRegionVal(Reg));3174 if (const MemRegion *FR = L.getAsRegion())3175 Out->insert(FR);3176 }3177 3178 std::optional<FieldConsumer> switchToBase(const CXXRecordDecl *BaseDecl,3179 bool IsVirtual) {3180 // Get the base class region3181 SVal BaseL =3182 C->getState()->getLValue(BaseDecl, Reg->getAs<SubRegion>(), IsVirtual);3183 if (const MemRegion *BaseObjRegion = BaseL.getAsRegion()) {3184 // Return a consumer for the base class3185 return FieldConsumer{BaseObjRegion, *C, *Out};3186 }3187 return std::nullopt;3188 }3189};3190 3191/// Check if a record type has smart owning pointer fields (directly or in base3192/// classes). When FC is provided, also collect the field regions.3193///3194/// This function has dual behavior:3195/// - When FC is nullopt: Returns true if smart pointer fields are found3196/// - When FC is provided: Always returns false, but collects field regions3197/// as a side effect through the FieldConsumer3198///3199/// Note: When FC is provided, the return value should be ignored since the3200/// function performs full traversal for collection and always returns false3201/// to avoid early termination.3202static bool hasSmartPtrField(const CXXRecordDecl *CRD,3203 std::optional<FieldConsumer> FC = std::nullopt) {3204 // Check direct fields3205 for (const FieldDecl *FD : CRD->fields()) {3206 if (isSmartPtrType(FD->getType())) {3207 if (!FC)3208 return true;3209 FC->consume(FD);3210 }3211 }3212 3213 // Check fields from base classes3214 for (const CXXBaseSpecifier &BaseSpec : CRD->bases()) {3215 if (const CXXRecordDecl *BaseDecl =3216 BaseSpec.getType()->getAsCXXRecordDecl()) {3217 std::optional<FieldConsumer> NewFC;3218 if (FC) {3219 NewFC = FC->switchToBase(BaseDecl, BaseSpec.isVirtual());3220 if (!NewFC)3221 continue;3222 }3223 bool Found = hasSmartPtrField(BaseDecl, NewFC);3224 if (Found && !FC)3225 return true;3226 }3227 }3228 return false;3229}3230 3231/// Check if an expression is an rvalue record type passed by value.3232static bool isRvalueByValueRecord(const Expr *AE) {3233 if (AE->isGLValue())3234 return false;3235 3236 QualType T = AE->getType();3237 if (!T->isRecordType() || T->isReferenceType())3238 return false;3239 3240 // Accept common temp/construct forms but don't overfit.3241 return isa<CXXTemporaryObjectExpr, MaterializeTemporaryExpr, CXXConstructExpr,3242 InitListExpr, ImplicitCastExpr, CXXBindTemporaryExpr>(AE);3243}3244 3245/// Check if an expression is an rvalue record with smart owning pointer fields3246/// passed by value.3247static bool isRvalueByValueRecordWithSmartPtr(const Expr *AE) {3248 if (!isRvalueByValueRecord(AE))3249 return false;3250 3251 const auto *CRD = AE->getType()->getAsCXXRecordDecl();3252 return CRD && hasSmartPtrField(CRD);3253}3254 3255/// Check if a CXXRecordDecl has a name matching recognized smart pointer names.3256static bool isSmartPtrRecord(const CXXRecordDecl *RD) {3257 if (!RD)3258 return false;3259 3260 // Check the record name directly and accept both std and custom smart pointer3261 // implementations for broader coverage3262 return isSmartPtrName(RD->getName());3263}3264 3265/// Check if a call is a constructor of a smart owning pointer class that3266/// accepts pointer parameters.3267static bool isSmartPtrCall(const CallEvent &Call) {3268 // Only check for smart pointer constructor calls3269 const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Call.getDecl());3270 if (!CD)3271 return false;3272 3273 const auto *RD = CD->getParent();3274 if (!isSmartPtrRecord(RD))3275 return false;3276 3277 // Check if constructor takes a pointer parameter3278 for (const auto *Param : CD->parameters()) {3279 QualType ParamType = Param->getType();3280 if (ParamType->isPointerType() && !ParamType->isFunctionPointerType() &&3281 !ParamType->isVoidPointerType()) {3282 return true;3283 }3284 }3285 3286 return false;3287}3288 3289/// Collect memory regions of smart owning pointer fields from a record type3290/// (including fields from base classes).3291static void3292collectSmartPtrFieldRegions(const MemRegion *Reg, QualType RecQT,3293 CheckerContext &C,3294 llvm::SmallPtrSetImpl<const MemRegion *> &Out) {3295 if (!Reg)3296 return;3297 3298 const auto *CRD = RecQT->getAsCXXRecordDecl();3299 if (!CRD)3300 return;3301 3302 FieldConsumer FC{Reg, C, Out};3303 hasSmartPtrField(CRD, FC);3304}3305 3306/// Handle smart pointer constructor calls by escaping allocated symbols3307/// that are passed as pointer arguments to the constructor.3308ProgramStateRef MallocChecker::handleSmartPointerConstructorArguments(3309 const CallEvent &Call, ProgramStateRef State) const {3310 const auto *CD = cast<CXXConstructorDecl>(Call.getDecl());3311 for (unsigned I = 0, E = std::min(Call.getNumArgs(), CD->getNumParams());3312 I != E; ++I) {3313 const Expr *ArgExpr = Call.getArgExpr(I);3314 if (!ArgExpr)3315 continue;3316 3317 QualType ParamType = CD->getParamDecl(I)->getType();3318 if (ParamType->isPointerType() && !ParamType->isFunctionPointerType() &&3319 !ParamType->isVoidPointerType()) {3320 // This argument is a pointer being passed to smart pointer constructor3321 SVal ArgVal = Call.getArgSVal(I);3322 SymbolRef Sym = ArgVal.getAsSymbol();3323 if (Sym && State->contains<RegionState>(Sym)) {3324 const RefState *RS = State->get<RegionState>(Sym);3325 if (RS && (RS->isAllocated() || RS->isAllocatedOfSizeZero())) {3326 State = State->set<RegionState>(Sym, RefState::getEscaped(RS));3327 }3328 }3329 }3330 }3331 return State;3332}3333 3334/// Handle all smart pointer related processing in function calls.3335/// This includes both direct smart pointer constructor calls and by-value3336/// arguments containing smart pointer fields.3337ProgramStateRef MallocChecker::handleSmartPointerRelatedCalls(3338 const CallEvent &Call, CheckerContext &C, ProgramStateRef State) const {3339 3340 // Handle direct smart pointer constructor calls first3341 if (isSmartPtrCall(Call)) {3342 return handleSmartPointerConstructorArguments(Call, State);3343 }3344 3345 // Handle smart pointer fields in by-value record arguments3346 llvm::SmallPtrSet<const MemRegion *, 8> SmartPtrFieldRoots;3347 for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {3348 const Expr *AE = Call.getArgExpr(I);3349 if (!AE)3350 continue;3351 AE = AE->IgnoreParenImpCasts();3352 3353 if (!isRvalueByValueRecordWithSmartPtr(AE))3354 continue;3355 3356 // Find a region for the argument.3357 SVal ArgVal = Call.getArgSVal(I);3358 const MemRegion *ArgRegion = ArgVal.getAsRegion();3359 // Collect direct smart owning pointer field regions3360 collectSmartPtrFieldRegions(ArgRegion, AE->getType(), C,3361 SmartPtrFieldRoots);3362 }3363 3364 // Escape symbols reachable from smart pointer fields3365 if (!SmartPtrFieldRoots.empty()) {3366 SmallVector<const MemRegion *, 8> SmartPtrFieldRootsVec(3367 SmartPtrFieldRoots.begin(), SmartPtrFieldRoots.end());3368 State = EscapeTrackedCallback::EscapeTrackedRegionsReachableFrom(3369 SmartPtrFieldRootsVec, State);3370 }3371 3372 return State;3373}3374 3375void MallocChecker::checkPostCall(const CallEvent &Call,3376 CheckerContext &C) const {3377 // Handle existing post-call handlers first3378 if (const auto *PostFN = PostFnMap.lookup(Call)) {3379 (*PostFN)(this, C.getState(), Call, C);3380 return; // Post-handler already called addTransition, we're done3381 }3382 3383 // Handle smart pointer related processing only if no post-handler was called3384 C.addTransition(handleSmartPointerRelatedCalls(Call, C, C.getState()));3385}3386 3387void MallocChecker::checkPreCall(const CallEvent &Call,3388 CheckerContext &C) const {3389 3390 if (const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call)) {3391 const CXXDeleteExpr *DE = DC->getOriginExpr();3392 3393 // FIXME: I don't see a good reason for restricting the check against3394 // use-after-free violations to the case when NewDeleteChecker is disabled.3395 // (However, if NewDeleteChecker is enabled, perhaps it would be better to3396 // do this check a bit later?)3397 if (!NewDeleteChecker.isEnabled())3398 if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())3399 checkUseAfterFree(Sym, C, DE->getArgument());3400 3401 if (!isStandardNewDelete(DC->getDecl()))3402 return;3403 3404 ProgramStateRef State = C.getState();3405 bool IsKnownToBeAllocated;3406 State = FreeMemAux(3407 C, DE->getArgument(), Call, State,3408 /*Hold*/ false, IsKnownToBeAllocated,3409 AllocationFamily(DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew));3410 3411 C.addTransition(State);3412 return;3413 }3414 3415 // If we see a `CXXDestructorCall` (that is, an _implicit_ destructor call)3416 // to a region that's symbolic and known to be already freed, then it must be3417 // implicitly triggered by a `delete` expression. In this situation we should3418 // emit a `DoubleFree` report _now_ (before entering the call to the3419 // destructor) because otherwise the destructor call can trigger a3420 // use-after-free bug (by accessing any member variable) and that would be3421 // (technically valid, but) less user-friendly report than the `DoubleFree`.3422 if (const auto *DC = dyn_cast<CXXDestructorCall>(&Call)) {3423 SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();3424 if (!Sym)3425 return;3426 if (isReleased(Sym, C)) {3427 HandleDoubleFree(C, SourceRange(), /*Released=*/true, Sym,3428 /*PrevSym=*/nullptr);3429 return;3430 }3431 }3432 3433 // We need to handle getline pre-conditions here before the pointed region3434 // gets invalidated by StreamChecker3435 if (const auto *PreFN = PreFnMap.lookup(Call)) {3436 (*PreFN)(this, C.getState(), Call, C);3437 return;3438 }3439 3440 // We will check for double free in the `evalCall` callback.3441 // FIXME: It would be more logical to emit double free and use-after-free3442 // reports via the same pathway (because double free is essentially a specia3443 // case of use-after-free).3444 if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {3445 const FunctionDecl *FD = FC->getDecl();3446 if (!FD)3447 return;3448 3449 // FIXME: I suspect we should remove `MallocChecker.isEnabled() &&` because3450 // it's fishy that the enabled/disabled state of one frontend may influence3451 // reports produced by other frontends.3452 if (MallocChecker.isEnabled() && isFreeingCall(Call))3453 return;3454 }3455 3456 // Check if the callee of a method is deleted.3457 if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {3458 SymbolRef Sym = CC->getCXXThisVal().getAsSymbol();3459 if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr()))3460 return;3461 }3462 3463 // Check arguments for being used after free.3464 for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {3465 SVal ArgSVal = Call.getArgSVal(I);3466 if (isa<Loc>(ArgSVal)) {3467 SymbolRef Sym = ArgSVal.getAsSymbol(/*IncludeBaseRegions=*/true);3468 if (!Sym)3469 continue;3470 if (checkUseAfterFree(Sym, C, Call.getArgExpr(I)))3471 return;3472 }3473 }3474}3475 3476void MallocChecker::checkPreStmt(const ReturnStmt *S,3477 CheckerContext &C) const {3478 checkEscapeOnReturn(S, C);3479}3480 3481// In the CFG, automatic destructors come after the return statement.3482// This callback checks for returning memory that is freed by automatic3483// destructors, as those cannot be reached in checkPreStmt().3484void MallocChecker::checkEndFunction(const ReturnStmt *S,3485 CheckerContext &C) const {3486 checkEscapeOnReturn(S, C);3487}3488 3489void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,3490 CheckerContext &C) const {3491 if (!S)3492 return;3493 3494 const Expr *E = S->getRetValue();3495 if (!E)3496 return;3497 3498 // Check if we are returning a symbol.3499 ProgramStateRef State = C.getState();3500 SVal RetVal = C.getSVal(E);3501 SymbolRef Sym = RetVal.getAsSymbol();3502 if (!Sym)3503 // If we are returning a field of the allocated struct or an array element,3504 // the callee could still free the memory.3505 if (const MemRegion *MR = RetVal.getAsRegion())3506 if (isa<FieldRegion, ElementRegion>(MR))3507 if (const SymbolicRegion *BMR =3508 dyn_cast<SymbolicRegion>(MR->getBaseRegion()))3509 Sym = BMR->getSymbol();3510 3511 // Check if we are returning freed memory.3512 if (Sym)3513 checkUseAfterFree(Sym, C, E);3514}3515 3516// TODO: Blocks should be either inlined or should call invalidate regions3517// upon invocation. After that's in place, special casing here will not be3518// needed.3519void MallocChecker::checkPostStmt(const BlockExpr *BE,3520 CheckerContext &C) const {3521 3522 // Scan the BlockDecRefExprs for any object the retain count checker3523 // may be tracking.3524 if (!BE->getBlockDecl()->hasCaptures())3525 return;3526 3527 ProgramStateRef state = C.getState();3528 const BlockDataRegion *R =3529 cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());3530 3531 auto ReferencedVars = R->referenced_vars();3532 if (ReferencedVars.empty())3533 return;3534 3535 SmallVector<const MemRegion*, 10> Regions;3536 const LocationContext *LC = C.getLocationContext();3537 MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();3538 3539 for (const auto &Var : ReferencedVars) {3540 const VarRegion *VR = Var.getCapturedRegion();3541 if (VR->getSuperRegion() == R) {3542 VR = MemMgr.getVarRegion(VR->getDecl(), LC);3543 }3544 Regions.push_back(VR);3545 }3546 3547 state =3548 state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();3549 C.addTransition(state);3550}3551 3552static bool isReleased(SymbolRef Sym, CheckerContext &C) {3553 assert(Sym);3554 const RefState *RS = C.getState()->get<RegionState>(Sym);3555 return (RS && RS->isReleased());3556}3557 3558bool MallocChecker::suppressDeallocationsInSuspiciousContexts(3559 const CallEvent &Call, CheckerContext &C) const {3560 if (Call.getNumArgs() == 0)3561 return false;3562 3563 StringRef FunctionStr = "";3564 if (const auto *FD = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))3565 if (const Stmt *Body = FD->getBody())3566 if (Body->getBeginLoc().isValid())3567 FunctionStr =3568 Lexer::getSourceText(CharSourceRange::getTokenRange(3569 {FD->getBeginLoc(), Body->getBeginLoc()}),3570 C.getSourceManager(), C.getLangOpts());3571 3572 // We do not model the Integer Set Library's retain-count based allocation.3573 if (!FunctionStr.contains("__isl_"))3574 return false;3575 3576 ProgramStateRef State = C.getState();3577 3578 for (const Expr *Arg : cast<CallExpr>(Call.getOriginExpr())->arguments())3579 if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol())3580 if (const RefState *RS = State->get<RegionState>(Sym))3581 State = State->set<RegionState>(Sym, RefState::getEscaped(RS));3582 3583 C.addTransition(State);3584 return true;3585}3586 3587bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,3588 const Stmt *S) const {3589 3590 if (isReleased(Sym, C)) {3591 HandleUseAfterFree(C, S->getSourceRange(), Sym);3592 return true;3593 }3594 3595 return false;3596}3597 3598void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,3599 const Stmt *S) const {3600 assert(Sym);3601 3602 if (const RefState *RS = C.getState()->get<RegionState>(Sym)) {3603 if (RS->isAllocatedOfSizeZero())3604 HandleUseZeroAlloc(C, RS->getStmt()->getSourceRange(), Sym);3605 }3606 else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {3607 HandleUseZeroAlloc(C, S->getSourceRange(), Sym);3608 }3609}3610 3611// Check if the location is a freed symbolic region.3612void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,3613 CheckerContext &C) const {3614 SymbolRef Sym = l.getLocSymbolInBase();3615 if (Sym) {3616 checkUseAfterFree(Sym, C, S);3617 checkUseZeroAllocated(Sym, C, S);3618 }3619}3620 3621// If a symbolic region is assumed to NULL (or another constant), stop tracking3622// it - assuming that allocation failed on this path.3623ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,3624 SVal Cond,3625 bool Assumption) const {3626 RegionStateTy RS = state->get<RegionState>();3627 for (SymbolRef Sym : llvm::make_first_range(RS)) {3628 // If the symbol is assumed to be NULL, remove it from consideration.3629 ConstraintManager &CMgr = state->getConstraintManager();3630 ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym);3631 if (AllocFailed.isConstrainedTrue())3632 state = state->remove<RegionState>(Sym);3633 }3634 3635 // Realloc returns 0 when reallocation fails, which means that we should3636 // restore the state of the pointer being reallocated.3637 ReallocPairsTy RP = state->get<ReallocPairs>();3638 for (auto [Sym, ReallocPair] : RP) {3639 // If the symbol is assumed to be NULL, remove it from consideration.3640 ConstraintManager &CMgr = state->getConstraintManager();3641 ConditionTruthVal AllocFailed = CMgr.isNull(state, Sym);3642 if (!AllocFailed.isConstrainedTrue())3643 continue;3644 3645 SymbolRef ReallocSym = ReallocPair.ReallocatedSym;3646 if (const RefState *RS = state->get<RegionState>(ReallocSym)) {3647 if (RS->isReleased()) {3648 switch (ReallocPair.Kind) {3649 case OAR_ToBeFreedAfterFailure:3650 state = state->set<RegionState>(ReallocSym,3651 RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));3652 break;3653 case OAR_DoNotTrackAfterFailure:3654 state = state->remove<RegionState>(ReallocSym);3655 break;3656 default:3657 assert(ReallocPair.Kind == OAR_FreeOnFailure);3658 }3659 }3660 }3661 state = state->remove<ReallocPairs>(Sym);3662 }3663 3664 return state;3665}3666 3667bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(3668 const CallEvent *Call,3669 ProgramStateRef State,3670 SymbolRef &EscapingSymbol) const {3671 assert(Call);3672 EscapingSymbol = nullptr;3673 3674 // For now, assume that any C++ or block call can free memory.3675 // TODO: If we want to be more optimistic here, we'll need to make sure that3676 // regions escape to C++ containers. They seem to do that even now, but for3677 // mysterious reasons.3678 if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call))3679 return true;3680 3681 // Check Objective-C messages by selector name.3682 if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {3683 // If it's not a framework call, or if it takes a callback, assume it3684 // can free memory.3685 if (!Call->isInSystemHeader() || Call->argumentsMayEscape())3686 return true;3687 3688 // If it's a method we know about, handle it explicitly post-call.3689 // This should happen before the "freeWhenDone" check below.3690 if (isKnownDeallocObjCMethodName(*Msg))3691 return false;3692 3693 // If there's a "freeWhenDone" parameter, but the method isn't one we know3694 // about, we can't be sure that the object will use free() to deallocate the3695 // memory, so we can't model it explicitly. The best we can do is use it to3696 // decide whether the pointer escapes.3697 if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))3698 return *FreeWhenDone;3699 3700 // If the first selector piece ends with "NoCopy", and there is no3701 // "freeWhenDone" parameter set to zero, we know ownership is being3702 // transferred. Again, though, we can't be sure that the object will use3703 // free() to deallocate the memory, so we can't model it explicitly.3704 StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);3705 if (FirstSlot.ends_with("NoCopy"))3706 return true;3707 3708 // If the first selector starts with addPointer, insertPointer,3709 // or replacePointer, assume we are dealing with NSPointerArray or similar.3710 // This is similar to C++ containers (vector); we still might want to check3711 // that the pointers get freed by following the container itself.3712 if (FirstSlot.starts_with("addPointer") ||3713 FirstSlot.starts_with("insertPointer") ||3714 FirstSlot.starts_with("replacePointer") ||3715 FirstSlot == "valueWithPointer") {3716 return true;3717 }3718 3719 // We should escape receiver on call to 'init'. This is especially relevant3720 // to the receiver, as the corresponding symbol is usually not referenced3721 // after the call.3722 if (Msg->getMethodFamily() == OMF_init) {3723 EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();3724 return true;3725 }3726 3727 // Otherwise, assume that the method does not free memory.3728 // Most framework methods do not free memory.3729 return false;3730 }3731 3732 // At this point the only thing left to handle is straight function calls.3733 const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl();3734 if (!FD)3735 return true;3736 3737 // If it's one of the allocation functions we can reason about, we model3738 // its behavior explicitly.3739 if (isMemCall(*Call))3740 return false;3741 3742 // If it's not a system call, assume it frees memory.3743 if (!Call->isInSystemHeader())3744 return true;3745 3746 // White list the system functions whose arguments escape.3747 const IdentifierInfo *II = FD->getIdentifier();3748 if (!II)3749 return true;3750 StringRef FName = II->getName();3751 3752 // White list the 'XXXNoCopy' CoreFoundation functions.3753 // We specifically check these before3754 if (FName.ends_with("NoCopy")) {3755 // Look for the deallocator argument. We know that the memory ownership3756 // is not transferred only if the deallocator argument is3757 // 'kCFAllocatorNull'.3758 for (unsigned i = 1; i < Call->getNumArgs(); ++i) {3759 const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts();3760 if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {3761 StringRef DeallocatorName = DE->getFoundDecl()->getName();3762 if (DeallocatorName == "kCFAllocatorNull")3763 return false;3764 }3765 }3766 return true;3767 }3768 3769 // Associating streams with malloced buffers. The pointer can escape if3770 // 'closefn' is specified (and if that function does free memory),3771 // but it will not if closefn is not specified.3772 // Currently, we do not inspect the 'closefn' function (PR12101).3773 if (FName == "funopen")3774 if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))3775 return false;3776 3777 // Do not warn on pointers passed to 'setbuf' when used with std streams,3778 // these leaks might be intentional when setting the buffer for stdio.3779 // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer3780 if (FName == "setbuf" || FName =="setbuffer" ||3781 FName == "setlinebuf" || FName == "setvbuf") {3782 if (Call->getNumArgs() >= 1) {3783 const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();3784 if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))3785 if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))3786 if (D->getCanonicalDecl()->getName().contains("std"))3787 return true;3788 }3789 }3790 3791 // A bunch of other functions which either take ownership of a pointer or3792 // wrap the result up in a struct or object, meaning it can be freed later.3793 // (See RetainCountChecker.) Not all the parameters here are invalidated,3794 // but the Malloc checker cannot differentiate between them. The right way3795 // of doing this would be to implement a pointer escapes callback.3796 if (FName == "CGBitmapContextCreate" ||3797 FName == "CGBitmapContextCreateWithData" ||3798 FName == "CVPixelBufferCreateWithBytes" ||3799 FName == "CVPixelBufferCreateWithPlanarBytes" ||3800 FName == "OSAtomicEnqueue") {3801 return true;3802 }3803 3804 if (FName == "postEvent" &&3805 FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") {3806 return true;3807 }3808 3809 if (FName == "connectImpl" &&3810 FD->getQualifiedNameAsString() == "QObject::connectImpl") {3811 return true;3812 }3813 3814 if (FName == "singleShotImpl" &&3815 FD->getQualifiedNameAsString() == "QTimer::singleShotImpl") {3816 return true;3817 }3818 3819 // Protobuf function declared in `generated_message_util.h` that takes3820 // ownership of the second argument. As the first and third arguments are3821 // allocation arenas and won't be tracked by this checker, there is no reason3822 // to set `EscapingSymbol`. (Also, this is an implementation detail of3823 // Protobuf, so it's better to be a bit more permissive.)3824 if (FName == "GetOwnedMessageInternal") {3825 return true;3826 }3827 3828 // Handle cases where we know a buffer's /address/ can escape.3829 // Note that the above checks handle some special cases where we know that3830 // even though the address escapes, it's still our responsibility to free the3831 // buffer.3832 if (Call->argumentsMayEscape())3833 return true;3834 3835 // Otherwise, assume that the function does not free memory.3836 // Most system calls do not free the memory.3837 return false;3838}3839 3840ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,3841 const InvalidatedSymbols &Escaped,3842 const CallEvent *Call,3843 PointerEscapeKind Kind) const {3844 return checkPointerEscapeAux(State, Escaped, Call, Kind,3845 /*IsConstPointerEscape*/ false);3846}3847 3848ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,3849 const InvalidatedSymbols &Escaped,3850 const CallEvent *Call,3851 PointerEscapeKind Kind) const {3852 // If a const pointer escapes, it may not be freed(), but it could be deleted.3853 return checkPointerEscapeAux(State, Escaped, Call, Kind,3854 /*IsConstPointerEscape*/ true);3855}3856 3857static bool checkIfNewOrNewArrayFamily(const RefState *RS) {3858 return (RS->getAllocationFamily().Kind == AF_CXXNewArray ||3859 RS->getAllocationFamily().Kind == AF_CXXNew);3860}3861 3862ProgramStateRef MallocChecker::checkPointerEscapeAux(3863 ProgramStateRef State, const InvalidatedSymbols &Escaped,3864 const CallEvent *Call, PointerEscapeKind Kind,3865 bool IsConstPointerEscape) const {3866 // If we know that the call does not free memory, or we want to process the3867 // call later, keep tracking the top level arguments.3868 SymbolRef EscapingSymbol = nullptr;3869 if (Kind == PSK_DirectEscapeOnCall &&3870 !mayFreeAnyEscapedMemoryOrIsModeledExplicitly(Call, State,3871 EscapingSymbol) &&3872 !EscapingSymbol) {3873 return State;3874 }3875 3876 for (SymbolRef sym : Escaped) {3877 if (EscapingSymbol && EscapingSymbol != sym)3878 continue;3879 3880 if (const RefState *RS = State->get<RegionState>(sym))3881 if (RS->isAllocated() || RS->isAllocatedOfSizeZero())3882 if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS))3883 State = State->set<RegionState>(sym, RefState::getEscaped(RS));3884 }3885 return State;3886}3887 3888bool MallocChecker::isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,3889 SVal ArgVal) const {3890 if (!KernelZeroSizePtrValue)3891 KernelZeroSizePtrValue =3892 tryExpandAsInteger("ZERO_SIZE_PTR", C.getPreprocessor());3893 3894 const llvm::APSInt *ArgValKnown =3895 C.getSValBuilder().getKnownValue(State, ArgVal);3896 return ArgValKnown && *KernelZeroSizePtrValue &&3897 ArgValKnown->getSExtValue() == **KernelZeroSizePtrValue;3898}3899 3900static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,3901 ProgramStateRef prevState) {3902 ReallocPairsTy currMap = currState->get<ReallocPairs>();3903 ReallocPairsTy prevMap = prevState->get<ReallocPairs>();3904 3905 for (const ReallocPairsTy::value_type &Pair : prevMap) {3906 SymbolRef sym = Pair.first;3907 if (!currMap.lookup(sym))3908 return sym;3909 }3910 3911 return nullptr;3912}3913 3914static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) {3915 if (const IdentifierInfo *II = DD->getParent()->getIdentifier()) {3916 StringRef N = II->getName();3917 if (N.contains_insensitive("ptr") || N.contains_insensitive("pointer")) {3918 if (N.contains_insensitive("ref") || N.contains_insensitive("cnt") ||3919 N.contains_insensitive("intrusive") ||3920 N.contains_insensitive("shared") || N.ends_with_insensitive("rc")) {3921 return true;3922 }3923 }3924 }3925 return false;3926}3927 3928PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,3929 BugReporterContext &BRC,3930 PathSensitiveBugReport &BR) {3931 ProgramStateRef state = N->getState();3932 ProgramStateRef statePrev = N->getFirstPred()->getState();3933 3934 const RefState *RSCurr = state->get<RegionState>(Sym);3935 const RefState *RSPrev = statePrev->get<RegionState>(Sym);3936 3937 const Stmt *S = N->getStmtForDiagnostics();3938 // When dealing with containers, we sometimes want to give a note3939 // even if the statement is missing.3940 if (!S && (!RSCurr || RSCurr->getAllocationFamily().Kind != AF_InnerBuffer))3941 return nullptr;3942 3943 const LocationContext *CurrentLC = N->getLocationContext();3944 3945 // If we find an atomic fetch_add or fetch_sub within the function in which3946 // the pointer was released (before the release), this is likely a release3947 // point of reference-counted object (like shared pointer).3948 //3949 // Because we don't model atomics, and also because we don't know that the3950 // original reference count is positive, we should not report use-after-frees3951 // on objects deleted in such functions. This can probably be improved3952 // through better shared pointer modeling.3953 if (ReleaseFunctionLC && (ReleaseFunctionLC == CurrentLC ||3954 ReleaseFunctionLC->isParentOf(CurrentLC))) {3955 if (const auto *AE = dyn_cast<AtomicExpr>(S)) {3956 // Check for manual use of atomic builtins.3957 AtomicExpr::AtomicOp Op = AE->getOp();3958 if (Op == AtomicExpr::AO__c11_atomic_fetch_add ||3959 Op == AtomicExpr::AO__c11_atomic_fetch_sub) {3960 BR.markInvalid(getTag(), S);3961 // After report is considered invalid there is no need to proceed3962 // futher.3963 return nullptr;3964 }3965 } else if (const auto *CE = dyn_cast<CallExpr>(S)) {3966 // Check for `std::atomic` and such. This covers both regular method calls3967 // and operator calls.3968 if (const auto *MD =3969 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee())) {3970 const CXXRecordDecl *RD = MD->getParent();3971 // A bit wobbly with ".contains()" because it may be like3972 // "__atomic_base" or something.3973 if (StringRef(RD->getNameAsString()).contains("atomic")) {3974 BR.markInvalid(getTag(), S);3975 // After report is considered invalid there is no need to proceed3976 // futher.3977 return nullptr;3978 }3979 }3980 }3981 }3982 3983 // FIXME: We will eventually need to handle non-statement-based events3984 // (__attribute__((cleanup))).3985 3986 // Find out if this is an interesting point and what is the kind.3987 StringRef Msg;3988 std::unique_ptr<StackHintGeneratorForSymbol> StackHint = nullptr;3989 SmallString<256> Buf;3990 llvm::raw_svector_ostream OS(Buf);3991 3992 if (Mode == Normal) {3993 if (isAllocated(RSCurr, RSPrev, S)) {3994 Msg = "Memory is allocated";3995 StackHint = std::make_unique<StackHintGeneratorForSymbol>(3996 Sym, "Returned allocated memory");3997 } else if (isReleased(RSCurr, RSPrev, S)) {3998 const auto Family = RSCurr->getAllocationFamily();3999 switch (Family.Kind) {4000 case AF_Alloca:4001 case AF_Malloc:4002 case AF_Custom:4003 case AF_CXXNew:4004 case AF_CXXNewArray:4005 case AF_IfNameIndex:4006 Msg = "Memory is released";4007 StackHint = std::make_unique<StackHintGeneratorForSymbol>(4008 Sym, "Returning; memory was released");4009 break;4010 case AF_InnerBuffer: {4011 const MemRegion *ObjRegion =4012 allocation_state::getContainerObjRegion(statePrev, Sym);4013 const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);4014 QualType ObjTy = TypedRegion->getValueType();4015 OS << "Inner buffer of '" << ObjTy << "' ";4016 4017 if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {4018 OS << "deallocated by call to destructor";4019 StackHint = std::make_unique<StackHintGeneratorForSymbol>(4020 Sym, "Returning; inner buffer was deallocated");4021 } else {4022 OS << "reallocated by call to '";4023 const Stmt *S = RSCurr->getStmt();4024 if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {4025 OS << MemCallE->getMethodDecl()->getDeclName();4026 } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {4027 OS << OpCallE->getDirectCallee()->getDeclName();4028 } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {4029 auto &CEMgr = BRC.getStateManager().getCallEventManager();4030 CallEventRef<> Call =4031 CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});4032 if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))4033 OS << D->getDeclName();4034 else4035 OS << "unknown";4036 }4037 OS << "'";4038 StackHint = std::make_unique<StackHintGeneratorForSymbol>(4039 Sym, "Returning; inner buffer was reallocated");4040 }4041 Msg = OS.str();4042 break;4043 }4044 case AF_None:4045 assert(false && "Unhandled allocation family!");4046 return nullptr;4047 }4048 4049 // Record the stack frame that is _responsible_ for this memory release4050 // event. This will be used by the false positive suppression heuristics4051 // that recognize the release points of reference-counted objects.4052 //4053 // Usually (e.g. in C) we say that the _responsible_ stack frame is the4054 // current innermost stack frame:4055 ReleaseFunctionLC = CurrentLC->getStackFrame();4056 // ...but if the stack contains a destructor call, then we say that the4057 // outermost destructor stack frame is the _responsible_ one:4058 for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {4059 if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {4060 if (isReferenceCountingPointerDestructor(DD)) {4061 // This immediately looks like a reference-counting destructor.4062 // We're bad at guessing the original reference count of the4063 // object, so suppress the report for now.4064 BR.markInvalid(getTag(), DD);4065 4066 // After report is considered invalid there is no need to proceed4067 // futher.4068 return nullptr;4069 }4070 4071 // Switch suspection to outer destructor to catch patterns like:4072 // (note that class name is distorted to bypass4073 // isReferenceCountingPointerDestructor() logic)4074 //4075 // SmartPointr::~SmartPointr() {4076 // if (refcount.fetch_sub(1) == 1)4077 // release_resources();4078 // }4079 // void SmartPointr::release_resources() {4080 // free(buffer);4081 // }4082 //4083 // This way ReleaseFunctionLC will point to outermost destructor and4084 // it would be possible to catch wider range of FP.4085 //4086 // NOTE: it would be great to support smth like that in C, since4087 // currently patterns like following won't be supressed:4088 //4089 // void doFree(struct Data *data) { free(data); }4090 // void putData(struct Data *data)4091 // {4092 // if (refPut(data))4093 // doFree(data);4094 // }4095 ReleaseFunctionLC = LC->getStackFrame();4096 }4097 }4098 4099 } else if (isRelinquished(RSCurr, RSPrev, S)) {4100 Msg = "Memory ownership is transferred";4101 StackHint = std::make_unique<StackHintGeneratorForSymbol>(Sym, "");4102 } else if (hasReallocFailed(RSCurr, RSPrev, S)) {4103 Mode = ReallocationFailed;4104 Msg = "Reallocation failed";4105 StackHint = std::make_unique<StackHintGeneratorForReallocationFailed>(4106 Sym, "Reallocation failed");4107 4108 if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {4109 // Is it possible to fail two reallocs WITHOUT testing in between?4110 assert((!FailedReallocSymbol || FailedReallocSymbol == sym) &&4111 "We only support one failed realloc at a time.");4112 BR.markInteresting(sym);4113 FailedReallocSymbol = sym;4114 }4115 }4116 4117 // We are in a special mode if a reallocation failed later in the path.4118 } else if (Mode == ReallocationFailed) {4119 assert(FailedReallocSymbol && "No symbol to look for.");4120 4121 // Is this is the first appearance of the reallocated symbol?4122 if (!statePrev->get<RegionState>(FailedReallocSymbol)) {4123 // We're at the reallocation point.4124 Msg = "Attempt to reallocate memory";4125 StackHint = std::make_unique<StackHintGeneratorForSymbol>(4126 Sym, "Returned reallocated memory");4127 FailedReallocSymbol = nullptr;4128 Mode = Normal;4129 }4130 }4131 4132 if (Msg.empty()) {4133 assert(!StackHint);4134 return nullptr;4135 }4136 4137 assert(StackHint);4138 4139 // Generate the extra diagnostic.4140 PathDiagnosticLocation Pos;4141 if (!S) {4142 assert(RSCurr->getAllocationFamily().Kind == AF_InnerBuffer);4143 auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();4144 if (!PostImplCall)4145 return nullptr;4146 Pos = PathDiagnosticLocation(PostImplCall->getLocation(),4147 BRC.getSourceManager());4148 } else {4149 Pos = PathDiagnosticLocation(S, BRC.getSourceManager(),4150 N->getLocationContext());4151 }4152 4153 auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);4154 BR.addCallStackHint(P, std::move(StackHint));4155 return P;4156}4157 4158void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,4159 const char *NL, const char *Sep) const {4160 4161 RegionStateTy RS = State->get<RegionState>();4162 4163 if (!RS.isEmpty()) {4164 Out << Sep << "MallocChecker :" << NL;4165 for (auto [Sym, Data] : RS) {4166 const RefState *RefS = State->get<RegionState>(Sym);4167 AllocationFamily Family = RefS->getAllocationFamily();4168 4169 const CheckerFrontend *Frontend =4170 getRelevantFrontendAs<CheckerFrontend>(Family);4171 4172 Sym->dumpToStream(Out);4173 Out << " : ";4174 Data.dump(Out);4175 if (Frontend && Frontend->isEnabled())4176 Out << " (" << Frontend->getName() << ")";4177 Out << NL;4178 }4179 }4180}4181 4182namespace clang {4183namespace ento {4184namespace allocation_state {4185 4186ProgramStateRef4187markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {4188 AllocationFamily Family(AF_InnerBuffer);4189 return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));4190}4191 4192} // end namespace allocation_state4193} // end namespace ento4194} // end namespace clang4195 4196// Intended to be used in InnerPointerChecker to register the part of4197// MallocChecker connected to it.4198void ento::registerInnerPointerCheckerAux(CheckerManager &Mgr) {4199 Mgr.getChecker<MallocChecker>()->InnerPointerChecker.enable(Mgr);4200}4201 4202void ento::registerDynamicMemoryModeling(CheckerManager &Mgr) {4203 auto *Chk = Mgr.getChecker<MallocChecker>();4204 // FIXME: This is a "hidden" undocumented frontend but there are public4205 // checker options which are attached to it.4206 CheckerNameRef DMMName = Mgr.getCurrentCheckerName();4207 Chk->ShouldIncludeOwnershipAnnotatedFunctions =4208 Mgr.getAnalyzerOptions().getCheckerBooleanOption(DMMName, "Optimistic");4209 Chk->ShouldRegisterNoOwnershipChangeVisitor =4210 Mgr.getAnalyzerOptions().getCheckerBooleanOption(4211 DMMName, "AddNoOwnershipChangeNotes");4212}4213 4214bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) {4215 return true;4216}4217 4218#define REGISTER_CHECKER(NAME) \4219 void ento::register##NAME(CheckerManager &Mgr) { \4220 Mgr.getChecker<MallocChecker>()->NAME.enable(Mgr); \4221 } \4222 \4223 bool ento::shouldRegister##NAME(const CheckerManager &) { return true; }4224 4225// TODO: NewDelete and NewDeleteLeaks shouldn't be registered when not in C++.4226REGISTER_CHECKER(MallocChecker)4227REGISTER_CHECKER(NewDeleteChecker)4228REGISTER_CHECKER(NewDeleteLeaksChecker)4229REGISTER_CHECKER(MismatchedDeallocatorChecker)4230REGISTER_CHECKER(TaintedAllocChecker)4231