123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394 |
- //=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines malloc/free checker, which checks for potential memory
- // leaks, double free, and use-after-free problems.
- //
- //===----------------------------------------------------------------------===//
- #include "ClangSACheckers.h"
- #include "InterCheckerAPI.h"
- #include "clang/AST/Attr.h"
- #include "clang/Basic/SourceManager.h"
- #include "clang/Basic/TargetInfo.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/CheckerManager.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
- #include "llvm/ADT/ImmutableMap.h"
- #include "llvm/ADT/STLExtras.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/ADT/StringExtras.h"
- #include <climits>
- using namespace clang;
- using namespace ento;
- namespace {
- // Used to check correspondence between allocators and deallocators.
- enum AllocationFamily {
- AF_None,
- AF_Malloc,
- AF_CXXNew,
- AF_CXXNewArray
- };
- class RefState {
- enum Kind { // Reference to allocated memory.
- Allocated,
- // Reference to released/freed memory.
- Released,
- // The responsibility for freeing resources has transferred from
- // this reference. A relinquished symbol should not be freed.
- Relinquished,
- // We are no longer guaranteed to have observed all manipulations
- // of this pointer/memory. For example, it could have been
- // passed as a parameter to an opaque function.
- Escaped
- };
- const Stmt *S;
- unsigned K : 2; // Kind enum, but stored as a bitfield.
- unsigned Family : 30; // Rest of 32-bit word, currently just an allocation
- // family.
- RefState(Kind k, const Stmt *s, unsigned family)
- : S(s), K(k), Family(family) {
- assert(family != AF_None);
- }
- public:
- bool isAllocated() const { return K == Allocated; }
- bool isReleased() const { return K == Released; }
- bool isRelinquished() const { return K == Relinquished; }
- bool isEscaped() const { return K == Escaped; }
- AllocationFamily getAllocationFamily() const {
- return (AllocationFamily)Family;
- }
- const Stmt *getStmt() const { return S; }
- bool operator==(const RefState &X) const {
- return K == X.K && S == X.S && Family == X.Family;
- }
- static RefState getAllocated(unsigned family, const Stmt *s) {
- return RefState(Allocated, s, family);
- }
- static RefState getReleased(unsigned family, const Stmt *s) {
- return RefState(Released, s, family);
- }
- static RefState getRelinquished(unsigned family, const Stmt *s) {
- return RefState(Relinquished, s, family);
- }
- static RefState getEscaped(const RefState *RS) {
- return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily());
- }
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(K);
- ID.AddPointer(S);
- ID.AddInteger(Family);
- }
- void dump(raw_ostream &OS) const {
- switch (static_cast<Kind>(K)) {
- #define CASE(ID) case ID: OS << #ID; break;
- CASE(Allocated)
- CASE(Released)
- CASE(Relinquished)
- CASE(Escaped)
- }
- }
- LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
- };
- enum ReallocPairKind {
- RPToBeFreedAfterFailure,
- // The symbol has been freed when reallocation failed.
- RPIsFreeOnFailure,
- // The symbol does not need to be freed after reallocation fails.
- RPDoNotTrackAfterFailure
- };
- /// \class ReallocPair
- /// \brief Stores information about the symbol being reallocated by a call to
- /// 'realloc' to allow modeling failed reallocation later in the path.
- struct ReallocPair {
- // \brief The symbol which realloc reallocated.
- SymbolRef ReallocatedSym;
- ReallocPairKind Kind;
- ReallocPair(SymbolRef S, ReallocPairKind K) :
- ReallocatedSym(S), Kind(K) {}
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(Kind);
- ID.AddPointer(ReallocatedSym);
- }
- bool operator==(const ReallocPair &X) const {
- return ReallocatedSym == X.ReallocatedSym &&
- Kind == X.Kind;
- }
- };
- typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo;
- class MallocChecker : public Checker<check::DeadSymbols,
- check::PointerEscape,
- check::ConstPointerEscape,
- check::PreStmt<ReturnStmt>,
- check::PreCall,
- check::PostStmt<CallExpr>,
- check::PostStmt<CXXNewExpr>,
- check::PreStmt<CXXDeleteExpr>,
- check::PostStmt<BlockExpr>,
- check::PostObjCMessage,
- check::Location,
- eval::Assume>
- {
- public:
- MallocChecker()
- : II_malloc(nullptr), II_free(nullptr), II_realloc(nullptr),
- II_calloc(nullptr), II_valloc(nullptr), II_reallocf(nullptr),
- II_strndup(nullptr), II_strdup(nullptr), II_kmalloc(nullptr) {}
- /// In pessimistic mode, the checker assumes that it does not know which
- /// functions might free the memory.
- enum CheckKind {
- CK_MallocPessimistic,
- CK_MallocOptimistic,
- CK_NewDeleteChecker,
- CK_NewDeleteLeaksChecker,
- CK_MismatchedDeallocatorChecker,
- CK_NumCheckKinds
- };
- DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckName CheckNames[CK_NumCheckKinds];
- void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
- void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
- void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
- void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
- void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
- void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
- ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
- bool Assumption) const;
- void checkLocation(SVal l, bool isLoad, const Stmt *S,
- CheckerContext &C) const;
- ProgramStateRef checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const;
- ProgramStateRef checkConstPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const;
- void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const override;
- private:
- mutable std::unique_ptr<BugType> BT_DoubleFree[CK_NumCheckKinds];
- mutable std::unique_ptr<BugType> BT_DoubleDelete;
- mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds];
- mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds];
- mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds];
- mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
- mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
- mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
- *II_valloc, *II_reallocf, *II_strndup, *II_strdup,
- *II_kmalloc;
- mutable Optional<uint64_t> KernelZeroFlagVal;
- void initIdentifierInfo(ASTContext &C) const;
- /// \brief Determine family of a deallocation expression.
- AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const;
- /// \brief Print names of allocators and deallocators.
- ///
- /// \returns true on success.
- bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const;
- /// \brief Print expected name of an allocator based on the deallocator's
- /// family derived from the DeallocExpr.
- void printExpectedAllocName(raw_ostream &os, CheckerContext &C,
- const Expr *DeallocExpr) const;
- /// \brief Print expected name of a deallocator based on the allocator's
- /// family.
- void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
- ///@{
- /// Check if this is one of the functions which can allocate/reallocate memory
- /// pointed to by one of its arguments.
- bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
- bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
- bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
- bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
- ///@}
- ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr* Att) const;
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- const Expr *SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc) {
- return MallocMemAux(C, CE,
- State->getSVal(SizeEx, C.getLocationContext()),
- Init, State, Family);
- }
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- SVal SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
- // Check if this malloc() for special flags. At present that means M_ZERO or
- // __GFP_ZERO (in which case, treat it like calloc).
- llvm::Optional<ProgramStateRef>
- performKernelMalloc(const CallExpr *CE, CheckerContext &C,
- const ProgramStateRef &State) const;
- /// Update the RefState to reflect the new memory allocation.
- static ProgramStateRef
- MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
- ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr* Att) const;
- ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef state, unsigned Num,
- bool Hold,
- bool &ReleasedAllocated,
- bool ReturnsNullOnFailure = false) const;
- ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
- const Expr *ParentExpr,
- ProgramStateRef State,
- bool Hold,
- bool &ReleasedAllocated,
- bool ReturnsNullOnFailure = false) const;
- ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
- bool FreesMemOnFailure) const;
- static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE);
-
- ///\brief Check if the memory associated with this symbol was released.
- bool isReleased(SymbolRef Sym, CheckerContext &C) const;
- bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
- bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
- /// Check if the function is known free memory, or if it is
- /// "interesting" and should be modeled explicitly.
- ///
- /// \param [out] EscapingSymbol A function might not free memory in general,
- /// but could be known to free a particular symbol. In this case, false is
- /// returned and the single escaping symbol is returned through the out
- /// parameter.
- ///
- /// We assume that pointers do not escape through calls to system functions
- /// not handled by this checker.
- bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
- ProgramStateRef State,
- SymbolRef &EscapingSymbol) const;
- // Implementation of the checkPointerEscape callabcks.
- ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool(*CheckRefState)(const RefState*)) const;
- ///@{
- /// Tells if a given family/call/symbol is tracked by the current checker.
- /// Sets CheckKind to the kind of the checker responsible for this
- /// family/call/symbol.
- Optional<CheckKind> getCheckIfTracked(AllocationFamily Family) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const;
- ///@}
- static bool SummarizeValue(raw_ostream &os, SVal V);
- static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
- void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
- const Expr *DeallocExpr) const;
- void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
- const Expr *DeallocExpr, const RefState *RS,
- SymbolRef Sym, bool OwnershipTransferred) const;
- void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
- const Expr *DeallocExpr,
- const Expr *AllocExpr = nullptr) const;
- void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
- SymbolRef Sym) const;
- void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
- SymbolRef Sym, SymbolRef PrevSym) const;
- void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
- /// Find the location of the allocation for Sym on the path leading to the
- /// exploded node N.
- LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C) const;
- void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
- /// The bug visitor which allows us to print extra diagnostics along the
- /// BugReport path. For example, showing the allocation site of the leaked
- /// region.
- class MallocBugVisitor : public BugReporterVisitorImpl<MallocBugVisitor> {
- protected:
- enum NotificationMode {
- Normal,
- ReallocationFailed
- };
- // The allocated region symbol tracked by the main analysis.
- SymbolRef Sym;
- // The mode we are in, i.e. what kind of diagnostics will be emitted.
- NotificationMode Mode;
- // A symbol from when the primary region should have been reallocated.
- SymbolRef FailedReallocSymbol;
- bool IsLeak;
- public:
- MallocBugVisitor(SymbolRef S, bool isLeak = false)
- : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {}
- virtual ~MallocBugVisitor() {}
- void Profile(llvm::FoldingSetNodeID &ID) const override {
- static int X = 0;
- ID.AddPointer(&X);
- ID.AddPointer(Sym);
- }
- inline bool isAllocated(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> allocated. Other state (released) -> allocated.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
- (S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated()));
- }
- inline bool isReleased(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> released. Other state (allocated) -> released.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt)) &&
- (S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
- }
- inline bool isRelinquished(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> relinquished. Other state (allocated) -> relinquished.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
- isa<ObjCPropertyRefExpr>(Stmt)) &&
- (S && S->isRelinquished()) &&
- (!SPrev || !SPrev->isRelinquished()));
- }
- inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // If the expression is not a call, and the state change is
- // released -> allocated, it must be the realloc return value
- // check. If we have to handle more cases here, it might be cleaner just
- // to track this extra bit in the state itself.
- return ((!Stmt || !isa<CallExpr>(Stmt)) &&
- (S && S->isAllocated()) && (SPrev && !SPrev->isAllocated()));
- }
- PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext &BRC,
- BugReport &BR) override;
- PathDiagnosticPiece* getEndPath(BugReporterContext &BRC,
- const ExplodedNode *EndPathNode,
- BugReport &BR) override {
- if (!IsLeak)
- return nullptr;
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode,
- BRC.getSourceManager());
- // Do not add the statement itself as a range in case of leak.
- return new PathDiagnosticEventPiece(L, BR.getDescription(), false);
- }
- private:
- class StackHintGeneratorForReallocationFailed
- : public StackHintGeneratorForSymbol {
- public:
- StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
- : StackHintGeneratorForSymbol(S, M) {}
- std::string getMessageForArg(const Expr *ArgE,
- unsigned ArgIndex) override {
- // Printed parameters start at 1, not 0.
- ++ArgIndex;
- SmallString<200> buf;
- llvm::raw_svector_ostream os(buf);
- os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
- << " parameter failed";
- return os.str();
- }
- std::string getMessageForReturn(const CallExpr *CallExpr) override {
- return "Reallocation of returned value failed";
- }
- };
- };
- };
- } // end anonymous namespace
- REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
- REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
- // A map from the freed symbol to the symbol representing the return value of
- // the free function.
- REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
- namespace {
- class StopTrackingCallback : public SymbolVisitor {
- ProgramStateRef state;
- public:
- StopTrackingCallback(ProgramStateRef st) : state(st) {}
- ProgramStateRef getState() const { return state; }
- bool VisitSymbol(SymbolRef sym) override {
- state = state->remove<RegionState>(sym);
- return true;
- }
- };
- } // end anonymous namespace
- void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
- if (II_malloc)
- return;
- II_malloc = &Ctx.Idents.get("malloc");
- II_free = &Ctx.Idents.get("free");
- II_realloc = &Ctx.Idents.get("realloc");
- II_reallocf = &Ctx.Idents.get("reallocf");
- II_calloc = &Ctx.Idents.get("calloc");
- II_valloc = &Ctx.Idents.get("valloc");
- II_strdup = &Ctx.Idents.get("strdup");
- II_strndup = &Ctx.Idents.get("strndup");
- II_kmalloc = &Ctx.Idents.get("kmalloc");
- }
- bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
- if (isFreeFunction(FD, C))
- return true;
- if (isAllocationFunction(FD, C))
- return true;
- if (isStandardNewDelete(FD, C))
- return true;
- return false;
- }
- bool MallocChecker::isAllocationFunction(const FunctionDecl *FD,
- ASTContext &C) const {
- if (!FD)
- return false;
- if (FD->getKind() == Decl::Function) {
- IdentifierInfo *FunI = FD->getIdentifier();
- initIdentifierInfo(C);
- if (FunI == II_malloc || FunI == II_realloc ||
- FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc ||
- FunI == II_strdup || FunI == II_strndup || FunI == II_kmalloc)
- return true;
- }
- if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
- for (const auto *I : FD->specific_attrs<OwnershipAttr>())
- if (I->getOwnKind() == OwnershipAttr::Returns)
- return true;
- return false;
- }
- bool MallocChecker::isFreeFunction(const FunctionDecl *FD, ASTContext &C) const {
- if (!FD)
- return false;
- if (FD->getKind() == Decl::Function) {
- IdentifierInfo *FunI = FD->getIdentifier();
- initIdentifierInfo(C);
- if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf)
- return true;
- }
- if (ChecksEnabled[CK_MallocOptimistic] && FD->hasAttrs())
- for (const auto *I : FD->specific_attrs<OwnershipAttr>())
- if (I->getOwnKind() == OwnershipAttr::Takes ||
- I->getOwnKind() == OwnershipAttr::Holds)
- return true;
- return false;
- }
- // Tells if the callee is one of the following:
- // 1) A global non-placement new/delete operator function.
- // 2) A global placement operator function with the single placement argument
- // of type std::nothrow_t.
- bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
- ASTContext &C) const {
- if (!FD)
- return false;
- OverloadedOperatorKind Kind = FD->getOverloadedOperator();
- if (Kind != OO_New && Kind != OO_Array_New &&
- Kind != OO_Delete && Kind != OO_Array_Delete)
- return false;
- // Skip all operator new/delete methods.
- if (isa<CXXMethodDecl>(FD))
- return false;
- // Return true if tested operator is a standard placement nothrow operator.
- if (FD->getNumParams() == 2) {
- QualType T = FD->getParamDecl(1)->getType();
- if (const IdentifierInfo *II = T.getBaseTypeIdentifier())
- return II->getName().equals("nothrow_t");
- }
- // Skip placement operators.
- if (FD->getNumParams() != 1 || FD->isVariadic())
- return false;
- // One of the standard new/new[]/delete/delete[] non-placement operators.
- return true;
- }
- llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
- const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
- // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
- //
- // void *malloc(unsigned long size, struct malloc_type *mtp, int flags);
- //
- // One of the possible flags is M_ZERO, which means 'give me back an
- // allocation which is already zeroed', like calloc.
- // 2-argument kmalloc(), as used in the Linux kernel:
- //
- // void *kmalloc(size_t size, gfp_t flags);
- //
- // Has the similar flag value __GFP_ZERO.
- // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some
- // code could be shared.
- ASTContext &Ctx = C.getASTContext();
- llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();
- if (!KernelZeroFlagVal.hasValue()) {
- if (OS == llvm::Triple::FreeBSD)
- KernelZeroFlagVal = 0x0100;
- else if (OS == llvm::Triple::NetBSD)
- KernelZeroFlagVal = 0x0002;
- else if (OS == llvm::Triple::OpenBSD)
- KernelZeroFlagVal = 0x0008;
- else if (OS == llvm::Triple::Linux)
- // __GFP_ZERO
- KernelZeroFlagVal = 0x8000;
- else
- // FIXME: We need a more general way of getting the M_ZERO value.
- // See also: O_CREAT in UnixAPIChecker.cpp.
- // Fall back to normal malloc behavior on platforms where we don't
- // know M_ZERO.
- return None;
- }
- // We treat the last argument as the flags argument, and callers fall-back to
- // normal malloc on a None return. This works for the FreeBSD kernel malloc
- // as well as Linux kmalloc.
- if (CE->getNumArgs() < 2)
- return None;
- const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
- const SVal V = State->getSVal(FlagsEx, C.getLocationContext());
- if (!V.getAs<NonLoc>()) {
- // The case where 'V' can be a location can only be due to a bad header,
- // so in this case bail out.
- return None;
- }
- NonLoc Flags = V.castAs<NonLoc>();
- NonLoc ZeroFlag = C.getSValBuilder()
- .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType())
- .castAs<NonLoc>();
- SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
- Flags, ZeroFlag,
- FlagsEx->getType());
- if (MaskedFlagsUC.isUnknownOrUndef())
- return None;
- DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
- // Check if maskedFlags is non-zero.
- ProgramStateRef TrueState, FalseState;
- std::tie(TrueState, FalseState) = State->assume(MaskedFlags);
- // If M_ZERO is set, treat this like calloc (initialized).
- if (TrueState && !FalseState) {
- SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
- return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState);
- }
- return None;
- }
- void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
- if (C.wasInlined)
- return;
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
- return;
- ProgramStateRef State = C.getState();
- bool ReleasedAllocatedMemory = false;
- if (FD->getKind() == Decl::Function) {
- initIdentifierInfo(C.getASTContext());
- IdentifierInfo *FunI = FD->getIdentifier();
- if (FunI == II_malloc) {
- if (CE->getNumArgs() < 1)
- return;
- if (CE->getNumArgs() < 3) {
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (CE->getNumArgs() == 3) {
- llvm::Optional<ProgramStateRef> MaybeState =
- performKernelMalloc(CE, C, State);
- if (MaybeState.hasValue())
- State = MaybeState.getValue();
- else
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- }
- } else if (FunI == II_kmalloc) {
- llvm::Optional<ProgramStateRef> MaybeState =
- performKernelMalloc(CE, C, State);
- if (MaybeState.hasValue())
- State = MaybeState.getValue();
- else
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == II_valloc) {
- if (CE->getNumArgs() < 1)
- return;
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == II_realloc) {
- State = ReallocMem(C, CE, false);
- } else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true);
- } else if (FunI == II_calloc) {
- State = CallocMem(C, CE);
- } else if (FunI == II_free) {
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- } else if (FunI == II_strdup) {
- State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == II_strndup) {
- State = MallocUpdateRefState(C, CE, State);
- }
- else if (isStandardNewDelete(FD, C.getASTContext())) {
- // Process direct calls to operator new/new[]/delete/delete[] functions
- // as distinct from new/new[]/delete/delete[] expressions that are
- // processed by the checkPostStmt callbacks for CXXNewExpr and
- // CXXDeleteExpr.
- OverloadedOperatorKind K = FD->getOverloadedOperator();
- if (K == OO_New)
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNew);
- else if (K == OO_Array_New)
- State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
- AF_CXXNewArray);
- else if (K == OO_Delete || K == OO_Array_Delete)
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- else
- llvm_unreachable("not a new/delete operator");
- }
- }
- if (ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
- // Check all the attributes, if there are any.
- // There can be multiple of these attributes.
- if (FD->hasAttrs())
- for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
- switch (I->getOwnKind()) {
- case OwnershipAttr::Returns:
- State = MallocMemReturnsAttr(C, CE, I);
- break;
- case OwnershipAttr::Takes:
- case OwnershipAttr::Holds:
- State = FreeMemAttr(C, CE, I);
- break;
- }
- }
- }
- C.addTransition(State);
- }
- void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
- if (NE->getNumPlacementArgs())
- for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
- E = NE->placement_arg_end(); I != E; ++I)
- if (SymbolRef Sym = C.getSVal(*I).getAsSymbol())
- checkUseAfterFree(Sym, C, *I);
- if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
- return;
- ProgramStateRef State = C.getState();
- // The return value from operator new is bound to a specified initialization
- // value (if any) and we don't want to loose this value. So we call
- // MallocUpdateRefState() instead of MallocMemAux() which breakes the
- // existing binding.
- State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
- : AF_CXXNew);
- C.addTransition(State);
- }
- void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
- CheckerContext &C) const {
- if (!ChecksEnabled[CK_NewDeleteChecker])
- if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
- checkUseAfterFree(Sym, C, DE->getArgument());
- if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
- return;
- ProgramStateRef State = C.getState();
- bool ReleasedAllocated;
- State = FreeMemAux(C, DE->getArgument(), DE, State,
- /*Hold*/false, ReleasedAllocated);
- C.addTransition(State);
- }
- static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
- // If the first selector piece is one of the names below, assume that the
- // object takes ownership of the memory, promising to eventually deallocate it
- // with free().
- // Ex: [NSData dataWithBytesNoCopy:bytes length:10];
- // (...unless a 'freeWhenDone' parameter is false, but that's checked later.)
- StringRef FirstSlot = Call.getSelector().getNameForSlot(0);
- if (FirstSlot == "dataWithBytesNoCopy" ||
- FirstSlot == "initWithBytesNoCopy" ||
- FirstSlot == "initWithCharactersNoCopy")
- return true;
- return false;
- }
- static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
- Selector S = Call.getSelector();
- // FIXME: We should not rely on fully-constrained symbols being folded.
- for (unsigned i = 1; i < S.getNumArgs(); ++i)
- if (S.getNameForSlot(i).equals("freeWhenDone"))
- return !Call.getArgSVal(i).isZeroConstant();
- return None;
- }
- void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
- CheckerContext &C) const {
- if (C.wasInlined)
- return;
- if (!isKnownDeallocObjCMethodName(Call))
- return;
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
- if (!*FreeWhenDone)
- return;
- bool ReleasedAllocatedMemory;
- ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0),
- Call.getOriginExpr(), C.getState(),
- /*Hold=*/true, ReleasedAllocatedMemory,
- /*RetNullOnFailure=*/true);
- C.addTransition(State);
- }
- ProgramStateRef
- MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
- const OwnershipAttr *Att) const {
- if (Att->getModule() != II_malloc)
- return nullptr;
- OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
- if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState());
- }
- return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), C.getState());
- }
- ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
- const CallExpr *CE,
- SVal Size, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family) {
- // Bind the return value to the symbolic value from the heap region.
- // TODO: We could rewrite post visit to eval call; 'malloc' does not have
- // side effects other than what we model here.
- unsigned Count = C.blockCount();
- SValBuilder &svalBuilder = C.getSValBuilder();
- const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
- DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)
- .castAs<DefinedSVal>();
- State = State->BindExpr(CE, C.getLocationContext(), RetVal);
- // We expect the malloc functions to return a pointer.
- if (!RetVal.getAs<Loc>())
- return nullptr;
- // Fill the region with the initialization value.
- State = State->bindDefault(RetVal, Init);
- // Set the region's extent equal to the Size parameter.
- const SymbolicRegion *R =
- dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
- if (!R)
- return nullptr;
- if (Optional<DefinedOrUnknownSVal> DefinedSize =
- Size.getAs<DefinedOrUnknownSVal>()) {
- SValBuilder &svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
- DefinedOrUnknownSVal extentMatchesSize =
- svalBuilder.evalEQ(State, Extent, *DefinedSize);
- State = State->assume(extentMatchesSize, true);
- assert(State);
- }
-
- return MallocUpdateRefState(C, CE, State, Family);
- }
- ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
- const Expr *E,
- ProgramStateRef State,
- AllocationFamily Family) {
- // Get the return value.
- SVal retVal = State->getSVal(E, C.getLocationContext());
- // We expect the malloc functions to return a pointer.
- if (!retVal.getAs<Loc>())
- return nullptr;
- SymbolRef Sym = retVal.getAsLocSymbol();
- assert(Sym);
- // Set the symbol's state to Allocated.
- return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));
- }
- ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
- const CallExpr *CE,
- const OwnershipAttr *Att) const {
- if (Att->getModule() != II_malloc)
- return nullptr;
- ProgramStateRef State = C.getState();
- bool ReleasedAllocated = false;
- for (const auto &Arg : Att->args()) {
- ProgramStateRef StateI = FreeMemAux(C, CE, State, Arg,
- Att->getOwnKind() == OwnershipAttr::Holds,
- ReleasedAllocated);
- if (StateI)
- State = StateI;
- }
- return State;
- }
- ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
- const CallExpr *CE,
- ProgramStateRef state,
- unsigned Num,
- bool Hold,
- bool &ReleasedAllocated,
- bool ReturnsNullOnFailure) const {
- if (CE->getNumArgs() < (Num + 1))
- return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, state, Hold,
- ReleasedAllocated, ReturnsNullOnFailure);
- }
- /// Checks if the previous call to free on the given symbol failed - if free
- /// failed, returns true. Also, returns the corresponding return value symbol.
- static bool didPreviousFreeFail(ProgramStateRef State,
- SymbolRef Sym, SymbolRef &RetStatusSymbol) {
- const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);
- if (Ret) {
- assert(*Ret && "We should not store the null return symbol");
- ConstraintManager &CMgr = State->getConstraintManager();
- ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret);
- RetStatusSymbol = *Ret;
- return FreeFailed.isConstrainedTrue();
- }
- return false;
- }
- AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
- const Stmt *S) const {
- if (!S)
- return AF_None;
- if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- if (!FD)
- FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
- ASTContext &Ctx = C.getASTContext();
- if (isAllocationFunction(FD, Ctx) || isFreeFunction(FD, Ctx))
- return AF_Malloc;
- if (isStandardNewDelete(FD, Ctx)) {
- OverloadedOperatorKind Kind = FD->getOverloadedOperator();
- if (Kind == OO_New || Kind == OO_Delete)
- return AF_CXXNew;
- else if (Kind == OO_Array_New || Kind == OO_Array_Delete)
- return AF_CXXNewArray;
- }
- return AF_None;
- }
- if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S))
- return NE->isArray() ? AF_CXXNewArray : AF_CXXNew;
- if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S))
- return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew;
- if (isa<ObjCMessageExpr>(S))
- return AF_Malloc;
- return AF_None;
- }
- bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const {
- if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
- // FIXME: This doesn't handle indirect calls.
- const FunctionDecl *FD = CE->getDirectCallee();
- if (!FD)
- return false;
-
- os << *FD;
- if (!FD->isOverloadedOperator())
- os << "()";
- return true;
- }
- if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
- if (Msg->isInstanceMessage())
- os << "-";
- else
- os << "+";
- Msg->getSelector().print(os);
- return true;
- }
- if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
- os << "'"
- << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())
- << "'";
- return true;
- }
- if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
- os << "'"
- << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())
- << "'";
- return true;
- }
- return false;
- }
- void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const {
- AllocationFamily Family = getAllocationFamily(C, E);
- switch(Family) {
- case AF_Malloc: os << "malloc()"; return;
- case AF_CXXNew: os << "'new'"; return;
- case AF_CXXNewArray: os << "'new[]'"; return;
- case AF_None: llvm_unreachable("not a deallocation expression");
- }
- }
- void MallocChecker::printExpectedDeallocName(raw_ostream &os,
- AllocationFamily Family) const {
- switch(Family) {
- case AF_Malloc: os << "free()"; return;
- case AF_CXXNew: os << "'delete'"; return;
- case AF_CXXNewArray: os << "'delete[]'"; return;
- case AF_None: llvm_unreachable("suspicious AF_None argument");
- }
- }
- ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
- const Expr *ArgExpr,
- const Expr *ParentExpr,
- ProgramStateRef State,
- bool Hold,
- bool &ReleasedAllocated,
- bool ReturnsNullOnFailure) const {
- SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());
- if (!ArgVal.getAs<DefinedOrUnknownSVal>())
- return nullptr;
- DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();
- // Check for null dereferences.
- if (!location.getAs<Loc>())
- return nullptr;
- // The explicit NULL case, no operation is performed.
- ProgramStateRef notNullState, nullState;
- std::tie(notNullState, nullState) = State->assume(location);
- if (nullState && !notNullState)
- return nullptr;
- // Unknown values could easily be okay
- // Undefined values are handled elsewhere
- if (ArgVal.isUnknownOrUndef())
- return nullptr;
- const MemRegion *R = ArgVal.getAsRegion();
-
- // Nonlocs can't be freed, of course.
- // Non-region locations (labels and fixed addresses) also shouldn't be freed.
- if (!R) {
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
- return nullptr;
- }
-
- R = R->StripCasts();
-
- // Blocks might show up as heap data, but should not be free()d
- if (isa<BlockDataRegion>(R)) {
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
- return nullptr;
- }
-
- const MemSpaceRegion *MS = R->getMemorySpace();
-
- // Parameters, locals, statics, globals, and memory returned by alloca()
- // shouldn't be freed.
- if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
- // FIXME: at the time this code was written, malloc() regions were
- // represented by conjured symbols, which are all in UnknownSpaceRegion.
- // This means that there isn't actually anything from HeapSpaceRegion
- // that should be freed, even though we allow it here.
- // Of course, free() can work on memory allocated outside the current
- // function, so UnknownSpaceRegion is always a possibility.
- // False negatives are better than false positives.
-
- ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
- return nullptr;
- }
- const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion());
- // Various cases could lead to non-symbol values here.
- // For now, ignore them.
- if (!SrBase)
- return nullptr;
- SymbolRef SymBase = SrBase->getSymbol();
- const RefState *RsBase = State->get<RegionState>(SymBase);
- SymbolRef PreviousRetStatusSymbol = nullptr;
- if (RsBase) {
- // Check for double free first.
- if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
- !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
- ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
- SymBase, PreviousRetStatusSymbol);
- return nullptr;
- // If the pointer is allocated or escaped, but we are now trying to free it,
- // check that the call to free is proper.
- } else if (RsBase->isAllocated() || RsBase->isEscaped()) {
- // Check if an expected deallocation function matches the real one.
- bool DeallocMatchesAlloc =
- RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
- if (!DeallocMatchesAlloc) {
- ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
- ParentExpr, RsBase, SymBase, Hold);
- return nullptr;
- }
- // Check if the memory location being freed is the actual location
- // allocated, or an offset.
- RegionOffset Offset = R->getAsOffset();
- if (Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
- Offset.getOffset() != 0) {
- const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
- ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
- AllocExpr);
- return nullptr;
- }
- }
- }
- ReleasedAllocated = (RsBase != nullptr) && RsBase->isAllocated();
- // Clean out the info on previous call to free return info.
- State = State->remove<FreeReturnValue>(SymBase);
- // Keep track of the return value. If it is NULL, we will know that free
- // failed.
- if (ReturnsNullOnFailure) {
- SVal RetVal = C.getSVal(ParentExpr);
- SymbolRef RetStatusSymbol = RetVal.getAsSymbol();
- if (RetStatusSymbol) {
- C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol);
- State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol);
- }
- }
- AllocationFamily Family = RsBase ? RsBase->getAllocationFamily()
- : getAllocationFamily(C, ParentExpr);
- // Normal free.
- if (Hold)
- return State->set<RegionState>(SymBase,
- RefState::getRelinquished(Family,
- ParentExpr));
- return State->set<RegionState>(SymBase,
- RefState::getReleased(Family, ParentExpr));
- }
- Optional<MallocChecker::CheckKind>
- MallocChecker::getCheckIfTracked(AllocationFamily Family) const {
- switch (Family) {
- case AF_Malloc: {
- if (ChecksEnabled[CK_MallocOptimistic]) {
- return CK_MallocOptimistic;
- } else if (ChecksEnabled[CK_MallocPessimistic]) {
- return CK_MallocPessimistic;
- }
- return Optional<MallocChecker::CheckKind>();
- }
- case AF_CXXNew:
- case AF_CXXNewArray: {
- if (ChecksEnabled[CK_NewDeleteChecker]) {
- return CK_NewDeleteChecker;
- }
- return Optional<MallocChecker::CheckKind>();
- }
- case AF_None: {
- llvm_unreachable("no family");
- }
- }
- llvm_unreachable("unhandled family");
- }
- Optional<MallocChecker::CheckKind>
- MallocChecker::getCheckIfTracked(CheckerContext &C,
- const Stmt *AllocDeallocStmt) const {
- return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt));
- }
- Optional<MallocChecker::CheckKind>
- MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym) const {
- const RefState *RS = C.getState()->get<RegionState>(Sym);
- assert(RS);
- return getCheckIfTracked(RS->getAllocationFamily());
- }
- bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
- if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>())
- os << "an integer (" << IntVal->getValue() << ")";
- else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>())
- os << "a constant address (" << ConstAddr->getValue() << ")";
- else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
- os << "the address of the label '" << Label->getLabel()->getName() << "'";
- else
- return false;
-
- return true;
- }
- bool MallocChecker::SummarizeRegion(raw_ostream &os,
- const MemRegion *MR) {
- switch (MR->getKind()) {
- case MemRegion::FunctionTextRegionKind: {
- const NamedDecl *FD = cast<FunctionTextRegion>(MR)->getDecl();
- if (FD)
- os << "the address of the function '" << *FD << '\'';
- else
- os << "the address of a function";
- return true;
- }
- case MemRegion::BlockTextRegionKind:
- os << "block text";
- return true;
- case MemRegion::BlockDataRegionKind:
- // FIXME: where the block came from?
- os << "a block";
- return true;
- default: {
- const MemSpaceRegion *MS = MR->getMemorySpace();
-
- if (isa<StackLocalsSpaceRegion>(MS)) {
- const VarRegion *VR = dyn_cast<VarRegion>(MR);
- const VarDecl *VD;
- if (VR)
- VD = VR->getDecl();
- else
- VD = nullptr;
- if (VD)
- os << "the address of the local variable '" << VD->getName() << "'";
- else
- os << "the address of a local stack variable";
- return true;
- }
- if (isa<StackArgumentsSpaceRegion>(MS)) {
- const VarRegion *VR = dyn_cast<VarRegion>(MR);
- const VarDecl *VD;
- if (VR)
- VD = VR->getDecl();
- else
- VD = nullptr;
- if (VD)
- os << "the address of the parameter '" << VD->getName() << "'";
- else
- os << "the address of a parameter";
- return true;
- }
- if (isa<GlobalsSpaceRegion>(MS)) {
- const VarRegion *VR = dyn_cast<VarRegion>(MR);
- const VarDecl *VD;
- if (VR)
- VD = VR->getDecl();
- else
- VD = nullptr;
- if (VD) {
- if (VD->isStaticLocal())
- os << "the address of the static variable '" << VD->getName() << "'";
- else
- os << "the address of the global variable '" << VD->getName() << "'";
- } else
- os << "the address of a global variable";
- return true;
- }
- return false;
- }
- }
- }
- void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
- SourceRange Range,
- const Expr *DeallocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
- !ChecksEnabled[CK_NewDeleteChecker])
- return;
- Optional<MallocChecker::CheckKind> CheckKind =
- getCheckIfTracked(C, DeallocExpr);
- if (!CheckKind.hasValue())
- return;
- if (ExplodedNode *N = C.generateSink()) {
- if (!BT_BadFree[*CheckKind])
- BT_BadFree[*CheckKind].reset(
- new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error"));
- SmallString<100> buf;
- llvm::raw_svector_ostream os(buf);
- const MemRegion *MR = ArgVal.getAsRegion();
- while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
- MR = ER->getSuperRegion();
- if (MR && isa<AllocaRegion>(MR))
- os << "Memory allocated by alloca() should not be deallocated";
- else {
- os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
- os << "deallocator";
- os << " is ";
- bool Summarized = MR ? SummarizeRegion(os, MR)
- : SummarizeValue(os, ArgVal);
- if (Summarized)
- os << ", which is not memory allocated by ";
- else
- os << "not memory allocated by ";
- printExpectedAllocName(os, C, DeallocExpr);
- }
- BugReport *R = new BugReport(*BT_BadFree[*CheckKind], os.str(), N);
- R->markInteresting(MR);
- R->addRange(Range);
- C.emitReport(R);
- }
- }
- void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
- SourceRange Range,
- const Expr *DeallocExpr,
- const RefState *RS,
- SymbolRef Sym,
- bool OwnershipTransferred) const {
- if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
- return;
- if (ExplodedNode *N = C.generateSink()) {
- if (!BT_MismatchedDealloc)
- BT_MismatchedDealloc.reset(
- new BugType(CheckNames[CK_MismatchedDeallocatorChecker],
- "Bad deallocator", "Memory Error"));
- SmallString<100> buf;
- llvm::raw_svector_ostream os(buf);
- const Expr *AllocExpr = cast<Expr>(RS->getStmt());
- SmallString<20> AllocBuf;
- llvm::raw_svector_ostream AllocOs(AllocBuf);
- SmallString<20> DeallocBuf;
- llvm::raw_svector_ostream DeallocOs(DeallocBuf);
- if (OwnershipTransferred) {
- if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
- os << DeallocOs.str() << " cannot";
- else
- os << "Cannot";
- os << " take ownership of memory";
- if (printAllocDeallocName(AllocOs, C, AllocExpr))
- os << " allocated by " << AllocOs.str();
- } else {
- os << "Memory";
- if (printAllocDeallocName(AllocOs, C, AllocExpr))
- os << " allocated by " << AllocOs.str();
- os << " should be deallocated by ";
- printExpectedDeallocName(os, RS->getAllocationFamily());
- if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
- os << ", not " << DeallocOs.str();
- }
- BugReport *R = new BugReport(*BT_MismatchedDealloc, os.str(), N);
- R->markInteresting(Sym);
- R->addRange(Range);
- R->addVisitor(new MallocBugVisitor(Sym));
- C.emitReport(R);
- }
- }
- void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
- SourceRange Range, const Expr *DeallocExpr,
- const Expr *AllocExpr) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
- !ChecksEnabled[CK_NewDeleteChecker])
- return;
- Optional<MallocChecker::CheckKind> CheckKind =
- getCheckIfTracked(C, AllocExpr);
- if (!CheckKind.hasValue())
- return;
- ExplodedNode *N = C.generateSink();
- if (!N)
- return;
- if (!BT_OffsetFree[*CheckKind])
- BT_OffsetFree[*CheckKind].reset(
- new BugType(CheckNames[*CheckKind], "Offset free", "Memory Error"));
- SmallString<100> buf;
- llvm::raw_svector_ostream os(buf);
- SmallString<20> AllocNameBuf;
- llvm::raw_svector_ostream AllocNameOs(AllocNameBuf);
- const MemRegion *MR = ArgVal.getAsRegion();
- assert(MR && "Only MemRegion based symbols can have offset free errors");
- RegionOffset Offset = MR->getAsOffset();
- assert((Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
- Offset.getOffset() != 0) &&
- "Only symbols with a valid offset can have offset free errors");
- int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();
- os << "Argument to ";
- if (!printAllocDeallocName(os, C, DeallocExpr))
- os << "deallocator";
- os << " is offset by "
- << offsetBytes
- << " "
- << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
- << " from the start of ";
- if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr))
- os << "memory allocated by " << AllocNameOs.str();
- else
- os << "allocated memory";
- BugReport *R = new BugReport(*BT_OffsetFree[*CheckKind], os.str(), N);
- R->markInteresting(MR->getBaseRegion());
- R->addRange(Range);
- C.emitReport(R);
- }
- void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
- SymbolRef Sym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
- !ChecksEnabled[CK_NewDeleteChecker])
- return;
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
- return;
- if (ExplodedNode *N = C.generateSink()) {
- if (!BT_UseFree[*CheckKind])
- BT_UseFree[*CheckKind].reset(new BugType(
- CheckNames[*CheckKind], "Use-after-free", "Memory Error"));
- BugReport *R = new BugReport(*BT_UseFree[*CheckKind],
- "Use of memory after it is freed", N);
- R->markInteresting(Sym);
- R->addRange(Range);
- R->addVisitor(new MallocBugVisitor(Sym));
- C.emitReport(R);
- }
- }
- void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
- bool Released, SymbolRef Sym,
- SymbolRef PrevSym) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
- !ChecksEnabled[CK_NewDeleteChecker])
- return;
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
- return;
- if (ExplodedNode *N = C.generateSink()) {
- if (!BT_DoubleFree[*CheckKind])
- BT_DoubleFree[*CheckKind].reset(
- new BugType(CheckNames[*CheckKind], "Double free", "Memory Error"));
- BugReport *R =
- new BugReport(*BT_DoubleFree[*CheckKind],
- (Released ? "Attempt to free released memory"
- : "Attempt to free non-owned memory"),
- N);
- R->addRange(Range);
- R->markInteresting(Sym);
- if (PrevSym)
- R->markInteresting(PrevSym);
- R->addVisitor(new MallocBugVisitor(Sym));
- C.emitReport(R);
- }
- }
- void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
- if (!ChecksEnabled[CK_NewDeleteChecker])
- return;
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
- if (!CheckKind.hasValue())
- return;
- assert(*CheckKind == CK_NewDeleteChecker && "invalid check kind");
- if (ExplodedNode *N = C.generateSink()) {
- if (!BT_DoubleDelete)
- BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker],
- "Double delete", "Memory Error"));
- BugReport *R = new BugReport(*BT_DoubleDelete,
- "Attempt to delete released memory", N);
- R->markInteresting(Sym);
- R->addVisitor(new MallocBugVisitor(Sym));
- C.emitReport(R);
- }
- }
- ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
- const CallExpr *CE,
- bool FreesOnFail) const {
- if (CE->getNumArgs() < 2)
- return nullptr;
- ProgramStateRef state = C.getState();
- const Expr *arg0Expr = CE->getArg(0);
- const LocationContext *LCtx = C.getLocationContext();
- SVal Arg0Val = state->getSVal(arg0Expr, LCtx);
- if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
- return nullptr;
- DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
- SValBuilder &svalBuilder = C.getSValBuilder();
- DefinedOrUnknownSVal PtrEQ =
- svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull());
- // Get the size argument. If there is no size arg then give up.
- const Expr *Arg1 = CE->getArg(1);
- if (!Arg1)
- return nullptr;
- // Get the value of the size argument.
- SVal Arg1ValG = state->getSVal(Arg1, LCtx);
- if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())
- return nullptr;
- DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();
- // Compare the size argument to 0.
- DefinedOrUnknownSVal SizeZero =
- svalBuilder.evalEQ(state, Arg1Val,
- svalBuilder.makeIntValWithPtrWidth(0, false));
- ProgramStateRef StatePtrIsNull, StatePtrNotNull;
- std::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
- ProgramStateRef StateSizeIsZero, StateSizeNotZero;
- std::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero);
- // We only assume exceptional states if they are definitely true; if the
- // state is under-constrained, assume regular realloc behavior.
- bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
- bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;
- // If the ptr is NULL and the size is not 0, the call is equivalent to
- // malloc(size).
- if ( PrtIsNull && !SizeIsZero) {
- ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
- UndefinedVal(), StatePtrIsNull);
- return stateMalloc;
- }
- if (PrtIsNull && SizeIsZero)
- return nullptr;
- // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
- assert(!PrtIsNull);
- SymbolRef FromPtr = arg0Val.getAsSymbol();
- SVal RetVal = state->getSVal(CE, LCtx);
- SymbolRef ToPtr = RetVal.getAsSymbol();
- if (!FromPtr || !ToPtr)
- return nullptr;
- bool ReleasedAllocated = false;
- // If the size is 0, free the memory.
- if (SizeIsZero)
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
- false, ReleasedAllocated)){
- // The semantics of the return value are:
- // If size was equal to 0, either NULL or a pointer suitable to be passed
- // to free() is returned. We just free the input pointer and do not add
- // any constrains on the output pointer.
- return stateFree;
- }
- // Default behavior.
- if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) {
- ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
- UnknownVal(), stateFree);
- if (!stateRealloc)
- return nullptr;
- ReallocPairKind Kind = RPToBeFreedAfterFailure;
- if (FreesOnFail)
- Kind = RPIsFreeOnFailure;
- else if (!ReleasedAllocated)
- Kind = RPDoNotTrackAfterFailure;
- // Record the info about the reallocated symbol so that we could properly
- // process failed reallocation.
- stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
- ReallocPair(FromPtr, Kind));
- // The reallocated symbol should stay alive for as long as the new symbol.
- C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
- return stateRealloc;
- }
- return nullptr;
- }
- ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE){
- if (CE->getNumArgs() < 2)
- return nullptr;
- ProgramStateRef state = C.getState();
- SValBuilder &svalBuilder = C.getSValBuilder();
- const LocationContext *LCtx = C.getLocationContext();
- SVal count = state->getSVal(CE->getArg(0), LCtx);
- SVal elementSize = state->getSVal(CE->getArg(1), LCtx);
- SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize,
- svalBuilder.getContext().getSizeType());
- SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
- return MallocMemAux(C, CE, TotalSize, zeroVal, state);
- }
- LeakInfo
- MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C) const {
- const LocationContext *LeakContext = N->getLocationContext();
- // Walk the ExplodedGraph backwards and find the first node that referred to
- // the tracked symbol.
- const ExplodedNode *AllocNode = N;
- const MemRegion *ReferenceRegion = nullptr;
- while (N) {
- ProgramStateRef State = N->getState();
- if (!State->get<RegionState>(Sym))
- break;
- // Find the most recent expression bound to the symbol in the current
- // context.
- if (!ReferenceRegion) {
- if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
- SVal Val = State->getSVal(MR);
- if (Val.getAsLocSymbol() == Sym) {
- const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>();
- // Do not show local variables belonging to a function other than
- // where the error is reported.
- if (!VR ||
- (VR->getStackFrame() == LeakContext->getCurrentStackFrame()))
- ReferenceRegion = MR;
- }
- }
- }
- // Allocation node, is the last node in the current context in which the
- // symbol was tracked.
- if (N->getLocationContext() == LeakContext)
- AllocNode = N;
- N = N->pred_empty() ? nullptr : *(N->pred_begin());
- }
- return LeakInfo(AllocNode, ReferenceRegion);
- }
- void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
- CheckerContext &C) const {
- if (!ChecksEnabled[CK_MallocOptimistic] &&
- !ChecksEnabled[CK_MallocPessimistic] &&
- !ChecksEnabled[CK_NewDeleteLeaksChecker])
- return;
- const RefState *RS = C.getState()->get<RegionState>(Sym);
- assert(RS && "cannot leak an untracked symbol");
- AllocationFamily Family = RS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- if (!CheckKind.hasValue())
- return;
- // Special case for new and new[]; these are controlled by a separate checker
- // flag so that they can be selectively disabled.
- if (Family == AF_CXXNew || Family == AF_CXXNewArray)
- if (!ChecksEnabled[CK_NewDeleteLeaksChecker])
- return;
- assert(N);
- if (!BT_Leak[*CheckKind]) {
- BT_Leak[*CheckKind].reset(
- new BugType(CheckNames[*CheckKind], "Memory leak", "Memory Error"));
- // Leaks should not be reported if they are post-dominated by a sink:
- // (1) Sinks are higher importance bugs.
- // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending
- // with __noreturn functions such as assert() or exit(). We choose not
- // to report leaks on such paths.
- BT_Leak[*CheckKind]->setSuppressOnSink(true);
- }
- // Most bug reports are cached at the location where they occurred.
- // With leaks, we want to unique them by the location where they were
- // allocated, and only report a single path.
- PathDiagnosticLocation LocUsedForUniqueing;
- const ExplodedNode *AllocNode = nullptr;
- const MemRegion *Region = nullptr;
- std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
-
- ProgramPoint P = AllocNode->getLocation();
- const Stmt *AllocationStmt = nullptr;
- if (Optional<CallExitEnd> Exit = P.getAs<CallExitEnd>())
- AllocationStmt = Exit->getCalleeContext()->getCallSite();
- else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>())
- AllocationStmt = SP->getStmt();
- if (AllocationStmt)
- LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
- C.getSourceManager(),
- AllocNode->getLocationContext());
- SmallString<200> buf;
- llvm::raw_svector_ostream os(buf);
- if (Region && Region->canPrintPretty()) {
- os << "Potential leak of memory pointed to by ";
- Region->printPretty(os);
- } else {
- os << "Potential memory leak";
- }
- BugReport *R =
- new BugReport(*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing,
- AllocNode->getLocationContext()->getDecl());
- R->markInteresting(Sym);
- R->addVisitor(new MallocBugVisitor(Sym, true));
- C.emitReport(R);
- }
- void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const
- {
- if (!SymReaper.hasDeadSymbols())
- return;
- ProgramStateRef state = C.getState();
- RegionStateTy RS = state->get<RegionState>();
- RegionStateTy::Factory &F = state->get_context<RegionState>();
- SmallVector<SymbolRef, 2> Errors;
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first)) {
- if (I->second.isAllocated())
- Errors.push_back(I->first);
- // Remove the dead symbol from the map.
- RS = F.remove(RS, I->first);
- }
- }
-
- // Cleanup the Realloc Pairs Map.
- ReallocPairsTy RP = state->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first) ||
- SymReaper.isDead(I->second.ReallocatedSym)) {
- state = state->remove<ReallocPairs>(I->first);
- }
- }
- // Cleanup the FreeReturnValue Map.
- FreeReturnValueTy FR = state->get<FreeReturnValue>();
- for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) {
- if (SymReaper.isDead(I->first) ||
- SymReaper.isDead(I->second)) {
- state = state->remove<FreeReturnValue>(I->first);
- }
- }
- // Generate leak node.
- ExplodedNode *N = C.getPredecessor();
- if (!Errors.empty()) {
- static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak");
- N = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
- for (SmallVectorImpl<SymbolRef>::iterator
- I = Errors.begin(), E = Errors.end(); I != E; ++I) {
- reportLeak(*I, N, C);
- }
- }
- C.addTransition(state->set<RegionState>(RS), N);
- }
- void MallocChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
- if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) {
- SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();
- if (!Sym || checkDoubleDelete(Sym, C))
- return;
- }
- // We will check for double free in the post visit.
- if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
- const FunctionDecl *FD = FC->getDecl();
- if (!FD)
- return;
- if ((ChecksEnabled[CK_MallocOptimistic] ||
- ChecksEnabled[CK_MallocPessimistic]) &&
- isFreeFunction(FD, C.getASTContext()))
- return;
- if (ChecksEnabled[CK_NewDeleteChecker] &&
- isStandardNewDelete(FD, C.getASTContext()))
- return;
- }
- // Check if the callee of a method is deleted.
- if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
- SymbolRef Sym = CC->getCXXThisVal().getAsSymbol();
- if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr()))
- return;
- }
- // Check arguments for being used after free.
- for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
- SVal ArgSVal = Call.getArgSVal(I);
- if (ArgSVal.getAs<Loc>()) {
- SymbolRef Sym = ArgSVal.getAsSymbol();
- if (!Sym)
- continue;
- if (checkUseAfterFree(Sym, C, Call.getArgExpr(I)))
- return;
- }
- }
- }
- void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
- const Expr *E = S->getRetValue();
- if (!E)
- return;
- // Check if we are returning a symbol.
- ProgramStateRef State = C.getState();
- SVal RetVal = State->getSVal(E, C.getLocationContext());
- SymbolRef Sym = RetVal.getAsSymbol();
- if (!Sym)
- // If we are returning a field of the allocated struct or an array element,
- // the callee could still free the memory.
- // TODO: This logic should be a part of generic symbol escape callback.
- if (const MemRegion *MR = RetVal.getAsRegion())
- if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR))
- if (const SymbolicRegion *BMR =
- dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
- Sym = BMR->getSymbol();
- // Check if we are returning freed memory.
- if (Sym)
- checkUseAfterFree(Sym, C, E);
- }
- // TODO: Blocks should be either inlined or should call invalidate regions
- // upon invocation. After that's in place, special casing here will not be
- // needed.
- void MallocChecker::checkPostStmt(const BlockExpr *BE,
- CheckerContext &C) const {
- // Scan the BlockDecRefExprs for any object the retain count checker
- // may be tracking.
- if (!BE->getBlockDecl()->hasCaptures())
- return;
- ProgramStateRef state = C.getState();
- const BlockDataRegion *R =
- cast<BlockDataRegion>(state->getSVal(BE,
- C.getLocationContext()).getAsRegion());
- BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
- E = R->referenced_vars_end();
- if (I == E)
- return;
- SmallVector<const MemRegion*, 10> Regions;
- const LocationContext *LC = C.getLocationContext();
- MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
- for ( ; I != E; ++I) {
- const VarRegion *VR = I.getCapturedRegion();
- if (VR->getSuperRegion() == R) {
- VR = MemMgr.getVarRegion(VR->getDecl(), LC);
- }
- Regions.push_back(VR);
- }
- state =
- state->scanReachableSymbols<StopTrackingCallback>(Regions.data(),
- Regions.data() + Regions.size()).getState();
- C.addTransition(state);
- }
- bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
- assert(Sym);
- const RefState *RS = C.getState()->get<RegionState>(Sym);
- return (RS && RS->isReleased());
- }
- bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
- const Stmt *S) const {
- if (isReleased(Sym, C)) {
- ReportUseAfterFree(C, S->getSourceRange(), Sym);
- return true;
- }
- return false;
- }
- bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
- if (isReleased(Sym, C)) {
- ReportDoubleDelete(C, Sym);
- return true;
- }
- return false;
- }
- // Check if the location is a freed symbolic region.
- void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
- CheckerContext &C) const {
- SymbolRef Sym = l.getLocSymbolInBase();
- if (Sym)
- checkUseAfterFree(Sym, C, S);
- }
- // If a symbolic region is assumed to NULL (or another constant), stop tracking
- // it - assuming that allocation failed on this path.
- ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
- SVal Cond,
- bool Assumption) const {
- RegionStateTy RS = state->get<RegionState>();
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
- // If the symbol is assumed to be NULL, remove it from consideration.
- ConstraintManager &CMgr = state->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
- if (AllocFailed.isConstrainedTrue())
- state = state->remove<RegionState>(I.getKey());
- }
- // Realloc returns 0 when reallocation fails, which means that we should
- // restore the state of the pointer being reallocated.
- ReallocPairsTy RP = state->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
- // If the symbol is assumed to be NULL, remove it from consideration.
- ConstraintManager &CMgr = state->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
- if (!AllocFailed.isConstrainedTrue())
- continue;
- SymbolRef ReallocSym = I.getData().ReallocatedSym;
- if (const RefState *RS = state->get<RegionState>(ReallocSym)) {
- if (RS->isReleased()) {
- if (I.getData().Kind == RPToBeFreedAfterFailure)
- state = state->set<RegionState>(ReallocSym,
- RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
- else if (I.getData().Kind == RPDoNotTrackAfterFailure)
- state = state->remove<RegionState>(ReallocSym);
- else
- assert(I.getData().Kind == RPIsFreeOnFailure);
- }
- }
- state = state->remove<ReallocPairs>(I.getKey());
- }
- return state;
- }
- bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
- const CallEvent *Call,
- ProgramStateRef State,
- SymbolRef &EscapingSymbol) const {
- assert(Call);
- EscapingSymbol = nullptr;
- // For now, assume that any C++ or block call can free memory.
- // TODO: If we want to be more optimistic here, we'll need to make sure that
- // regions escape to C++ containers. They seem to do that even now, but for
- // mysterious reasons.
- if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
- return true;
- // Check Objective-C messages by selector name.
- if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
- // If it's not a framework call, or if it takes a callback, assume it
- // can free memory.
- if (!Call->isInSystemHeader() || Call->hasNonZeroCallbackArg())
- return true;
- // If it's a method we know about, handle it explicitly post-call.
- // This should happen before the "freeWhenDone" check below.
- if (isKnownDeallocObjCMethodName(*Msg))
- return false;
- // If there's a "freeWhenDone" parameter, but the method isn't one we know
- // about, we can't be sure that the object will use free() to deallocate the
- // memory, so we can't model it explicitly. The best we can do is use it to
- // decide whether the pointer escapes.
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
- return *FreeWhenDone;
- // If the first selector piece ends with "NoCopy", and there is no
- // "freeWhenDone" parameter set to zero, we know ownership is being
- // transferred. Again, though, we can't be sure that the object will use
- // free() to deallocate the memory, so we can't model it explicitly.
- StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
- if (FirstSlot.endswith("NoCopy"))
- return true;
- // If the first selector starts with addPointer, insertPointer,
- // or replacePointer, assume we are dealing with NSPointerArray or similar.
- // This is similar to C++ containers (vector); we still might want to check
- // that the pointers get freed by following the container itself.
- if (FirstSlot.startswith("addPointer") ||
- FirstSlot.startswith("insertPointer") ||
- FirstSlot.startswith("replacePointer") ||
- FirstSlot.equals("valueWithPointer")) {
- return true;
- }
- // We should escape receiver on call to 'init'. This is especially relevant
- // to the receiver, as the corresponding symbol is usually not referenced
- // after the call.
- if (Msg->getMethodFamily() == OMF_init) {
- EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();
- return true;
- }
- // Otherwise, assume that the method does not free memory.
- // Most framework methods do not free memory.
- return false;
- }
- // At this point the only thing left to handle is straight function calls.
- const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl();
- if (!FD)
- return true;
- ASTContext &ASTC = State->getStateManager().getContext();
- // If it's one of the allocation functions we can reason about, we model
- // its behavior explicitly.
- if (isMemFunction(FD, ASTC))
- return false;
- // If it's not a system call, assume it frees memory.
- if (!Call->isInSystemHeader())
- return true;
- // White list the system functions whose arguments escape.
- const IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return true;
- StringRef FName = II->getName();
- // White list the 'XXXNoCopy' CoreFoundation functions.
- // We specifically check these before
- if (FName.endswith("NoCopy")) {
- // Look for the deallocator argument. We know that the memory ownership
- // is not transferred only if the deallocator argument is
- // 'kCFAllocatorNull'.
- for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
- const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts();
- if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
- StringRef DeallocatorName = DE->getFoundDecl()->getName();
- if (DeallocatorName == "kCFAllocatorNull")
- return false;
- }
- }
- return true;
- }
- // Associating streams with malloced buffers. The pointer can escape if
- // 'closefn' is specified (and if that function does free memory),
- // but it will not if closefn is not specified.
- // Currently, we do not inspect the 'closefn' function (PR12101).
- if (FName == "funopen")
- if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
- return false;
- // Do not warn on pointers passed to 'setbuf' when used with std streams,
- // these leaks might be intentional when setting the buffer for stdio.
- // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName =="setbuffer" ||
- FName == "setlinebuf" || FName == "setvbuf") {
- if (Call->getNumArgs() >= 1) {
- const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
- if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
- if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
- return true;
- }
- }
- // A bunch of other functions which either take ownership of a pointer or
- // wrap the result up in a struct or object, meaning it can be freed later.
- // (See RetainCountChecker.) Not all the parameters here are invalidated,
- // but the Malloc checker cannot differentiate between them. The right way
- // of doing this would be to implement a pointer escapes callback.
- if (FName == "CGBitmapContextCreate" ||
- FName == "CGBitmapContextCreateWithData" ||
- FName == "CVPixelBufferCreateWithBytes" ||
- FName == "CVPixelBufferCreateWithPlanarBytes" ||
- FName == "OSAtomicEnqueue") {
- return true;
- }
- // Handle cases where we know a buffer's /address/ can escape.
- // Note that the above checks handle some special cases where we know that
- // even though the address escapes, it's still our responsibility to free the
- // buffer.
- if (Call->argumentsMayEscape())
- return true;
- // Otherwise, assume that the function does not free memory.
- // Most system calls do not free the memory.
- return false;
- }
- static bool retTrue(const RefState *RS) {
- return true;
- }
- static bool checkIfNewOrNewArrayFamily(const RefState *RS) {
- return (RS->getAllocationFamily() == AF_CXXNewArray ||
- RS->getAllocationFamily() == AF_CXXNew);
- }
- ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const {
- return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue);
- }
- ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const {
- return checkPointerEscapeAux(State, Escaped, Call, Kind,
- &checkIfNewOrNewArrayFamily);
- }
- ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool(*CheckRefState)(const RefState*)) const {
- // If we know that the call does not free memory, or we want to process the
- // call later, keep tracking the top level arguments.
- SymbolRef EscapingSymbol = nullptr;
- if (Kind == PSK_DirectEscapeOnCall &&
- !mayFreeAnyEscapedMemoryOrIsModeledExplicitly(Call, State,
- EscapingSymbol) &&
- !EscapingSymbol) {
- return State;
- }
- for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
- E = Escaped.end();
- I != E; ++I) {
- SymbolRef sym = *I;
- if (EscapingSymbol && EscapingSymbol != sym)
- continue;
-
- if (const RefState *RS = State->get<RegionState>(sym)) {
- if (RS->isAllocated() && CheckRefState(RS)) {
- State = State->remove<RegionState>(sym);
- State = State->set<RegionState>(sym, RefState::getEscaped(RS));
- }
- }
- }
- return State;
- }
- static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
- ProgramStateRef prevState) {
- ReallocPairsTy currMap = currState->get<ReallocPairs>();
- ReallocPairsTy prevMap = prevState->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end();
- I != E; ++I) {
- SymbolRef sym = I.getKey();
- if (!currMap.lookup(sym))
- return sym;
- }
- return nullptr;
- }
- PathDiagnosticPiece *
- MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext &BRC,
- BugReport &BR) {
- ProgramStateRef state = N->getState();
- ProgramStateRef statePrev = PrevN->getState();
- const RefState *RS = state->get<RegionState>(Sym);
- const RefState *RSPrev = statePrev->get<RegionState>(Sym);
- if (!RS)
- return nullptr;
- const Stmt *S = nullptr;
- const char *Msg = nullptr;
- StackHintGeneratorForSymbol *StackHint = nullptr;
- // Retrieve the associated statement.
- ProgramPoint ProgLoc = N->getLocation();
- if (Optional<StmtPoint> SP = ProgLoc.getAs<StmtPoint>()) {
- S = SP->getStmt();
- } else if (Optional<CallExitEnd> Exit = ProgLoc.getAs<CallExitEnd>()) {
- S = Exit->getCalleeContext()->getCallSite();
- } else if (Optional<BlockEdge> Edge = ProgLoc.getAs<BlockEdge>()) {
- // If an assumption was made on a branch, it should be caught
- // here by looking at the state transition.
- S = Edge->getSrc()->getTerminator();
- }
- if (!S)
- return nullptr;
- // FIXME: We will eventually need to handle non-statement-based events
- // (__attribute__((cleanup))).
- // Find out if this is an interesting point and what is the kind.
- if (Mode == Normal) {
- if (isAllocated(RS, RSPrev, S)) {
- Msg = "Memory is allocated";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returned allocated memory");
- } else if (isReleased(RS, RSPrev, S)) {
- Msg = "Memory is released";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returning; memory was released");
- } else if (isRelinquished(RS, RSPrev, S)) {
- Msg = "Memory ownership is transferred";
- StackHint = new StackHintGeneratorForSymbol(Sym, "");
- } else if (isReallocFailedCheck(RS, RSPrev, S)) {
- Mode = ReallocationFailed;
- Msg = "Reallocation failed";
- StackHint = new StackHintGeneratorForReallocationFailed(Sym,
- "Reallocation failed");
- if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {
- // Is it possible to fail two reallocs WITHOUT testing in between?
- assert((!FailedReallocSymbol || FailedReallocSymbol == sym) &&
- "We only support one failed realloc at a time.");
- BR.markInteresting(sym);
- FailedReallocSymbol = sym;
- }
- }
- // We are in a special mode if a reallocation failed later in the path.
- } else if (Mode == ReallocationFailed) {
- assert(FailedReallocSymbol && "No symbol to look for.");
- // Is this is the first appearance of the reallocated symbol?
- if (!statePrev->get<RegionState>(FailedReallocSymbol)) {
- // We're at the reallocation point.
- Msg = "Attempt to reallocate memory";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returned reallocated memory");
- FailedReallocSymbol = nullptr;
- Mode = Normal;
- }
- }
- if (!Msg)
- return nullptr;
- assert(StackHint);
- // Generate the extra diagnostic.
- PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
- N->getLocationContext());
- return new PathDiagnosticEventPiece(Pos, Msg, true, StackHint);
- }
- void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const {
- RegionStateTy RS = State->get<RegionState>();
- if (!RS.isEmpty()) {
- Out << Sep << "MallocChecker :" << NL;
- for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
- const RefState *RefS = State->get<RegionState>(I.getKey());
- AllocationFamily Family = RefS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
- I.getKey()->dumpToStream(Out);
- Out << " : ";
- I.getData().dump(Out);
- if (CheckKind.hasValue())
- Out << " (" << CheckNames[*CheckKind].getName() << ")";
- Out << NL;
- }
- }
- }
- void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
- registerCStringCheckerBasic(mgr);
- MallocChecker *checker = mgr.registerChecker<MallocChecker>();
- checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
- checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
- mgr.getCurrentCheckName();
- // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
- // checker.
- if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker])
- checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
- }
- #define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &mgr) { \
- registerCStringCheckerBasic(mgr); \
- MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
- checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
- checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
- }
- REGISTER_CHECKER(MallocPessimistic)
- REGISTER_CHECKER(MallocOptimistic)
- REGISTER_CHECKER(NewDeleteChecker)
- REGISTER_CHECKER(MismatchedDeallocatorChecker)
|