Index: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -300,16 +300,15 @@ let ParentPackage = CplusplusAlpha in { -def DanglingInternalBufferChecker : Checker<"DanglingInternalBuffer">, - HelpText<"Check for internal raw pointers of C++ standard library containers " - "used after deallocation">, - DescFile<"DanglingInternalBufferChecker.cpp">; - def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">, HelpText<"Reports destructions of polymorphic objects with a non-virtual " "destructor in their base class">, DescFile<"DeleteWithNonVirtualDtorChecker.cpp">; +def InnerPointerChecker : Checker<"InnerPointer">, + HelpText<"Check for inner pointers of C++ containers used after re/deallocation">, + DescFile<"InnerPointerChecker.cpp">; + def IteratorRangeChecker : Checker<"IteratorRange">, HelpText<"Check for iterators used outside their valid ranges">, DescFile<"IteratorChecker.cpp">; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/AllocationState.h =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/AllocationState.h +++ cfe/trunk/lib/StaticAnalyzer/Checkers/AllocationState.h @@ -23,8 +23,8 @@ /// This function provides an additional visitor that augments the bug report /// with information relevant to memory errors caused by the misuse of -/// AF_InternalBuffer symbols. -std::unique_ptr getDanglingBufferBRVisitor(SymbolRef Sym); +/// AF_InnerBuffer symbols. +std::unique_ptr getInnerPointerBRVisitor(SymbolRef Sym); } // end namespace allocation_state Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -27,7 +27,6 @@ CloneChecker.cpp ConversionChecker.cpp CXXSelfAssignmentChecker.cpp - DanglingInternalBufferChecker.cpp DeadStoresChecker.cpp DebugCheckers.cpp DeleteWithNonVirtualDtorChecker.cpp @@ -42,6 +41,7 @@ GenericTaintChecker.cpp GTestChecker.cpp IdenticalExprChecker.cpp + InnerPointerChecker.cpp IteratorChecker.cpp IvarInvalidationChecker.cpp LLVMConventionsChecker.cpp Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp @@ -1,253 +0,0 @@ -//=== DanglingInternalBufferChecker.cpp ---------------------------*- 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 a check that marks a raw pointer to a C++ container's -// inner buffer released when the object is destroyed. This information can -// be used by MallocChecker to detect use-after-free problems. -// -//===----------------------------------------------------------------------===// - -#include "AllocationState.h" -#include "ClangSACheckers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" -#include "clang/StaticAnalyzer/Core/Checker.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" - -using namespace clang; -using namespace ento; - -using PtrSet = llvm::ImmutableSet; - -// Associate container objects with a set of raw pointer symbols. -REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet) - -// This is a trick to gain access to PtrSet's Factory. -namespace clang { -namespace ento { -template <> -struct ProgramStateTrait : public ProgramStatePartialTrait { - static void *GDMIndex() { - static int Index = 0; - return &Index; - } -}; -} // end namespace ento -} // end namespace clang - -namespace { - -class DanglingInternalBufferChecker - : public Checker { - - CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, - InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, - ShrinkToFitFn, SwapFn; - -public: - class DanglingBufferBRVisitor : public BugReporterVisitor { - SymbolRef PtrToBuf; - - public: - DanglingBufferBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} - - static void *getTag() { - static int Tag = 0; - return &Tag; - } - - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddPointer(getTag()); - } - - std::shared_ptr VisitNode(const ExplodedNode *N, - const ExplodedNode *PrevN, - BugReporterContext &BRC, - BugReport &BR) override; - - // FIXME: Scan the map once in the visitor's constructor and do a direct - // lookup by region. - bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { - RawPtrMapTy Map = State->get(); - for (const auto Entry : Map) { - if (Entry.second.contains(Sym)) - return true; - } - return false; - } - }; - - DanglingInternalBufferChecker() - : AppendFn("append"), AssignFn("assign"), ClearFn("clear"), - CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"), - PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"), - ReserveFn("reserve"), ResizeFn("resize"), - ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {} - - /// Check whether the function called on the container object is a - /// member function that potentially invalidates pointers referring - /// to the objects's internal buffer. - bool mayInvalidateBuffer(const CallEvent &Call) const; - - /// Record the connection between the symbol returned by c_str() and the - /// corresponding string object region in the ProgramState. Mark the symbol - /// released if the string object is destroyed. - void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - - /// Clean up the ProgramState map. - void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; -}; - -} // end anonymous namespace - -// [string.require] -// -// "References, pointers, and iterators referring to the elements of a -// basic_string sequence may be invalidated by the following uses of that -// basic_string object: -// -// -- TODO: As an argument to any standard library function taking a reference -// to non-const basic_string as an argument. For example, as an argument to -// non-member functions swap(), operator>>(), and getline(), or as an argument -// to basic_string::swap(). -// -// -- Calling non-const member functions, except operator[], at, front, back, -// begin, rbegin, end, and rend." -// -bool DanglingInternalBufferChecker::mayInvalidateBuffer( - const CallEvent &Call) const { - if (const auto *MemOpCall = dyn_cast(&Call)) { - OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); - if (Opc == OO_Equal || Opc == OO_PlusEqual) - return true; - return false; - } - return (isa(Call) || Call.isCalled(AppendFn) || - Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || - Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || - Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || - Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || - Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || - Call.isCalled(SwapFn)); -} - -void DanglingInternalBufferChecker::checkPostCall(const CallEvent &Call, - CheckerContext &C) const { - const auto *ICall = dyn_cast(&Call); - if (!ICall) - return; - - SVal Obj = ICall->getCXXThisVal(); - const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion()); - if (!ObjRegion) - return; - - auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl(); - if (TypeDecl->getName() != "basic_string") - return; - - ProgramStateRef State = C.getState(); - - if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { - SVal RawPtr = Call.getReturnValue(); - if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { - // Start tracking this raw pointer by adding it to the set of symbols - // associated with this container object in the program state map. - PtrSet::Factory &F = State->getStateManager().get_context(); - const PtrSet *SetPtr = State->get(ObjRegion); - PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); - assert(C.wasInlined || !Set.contains(Sym)); - Set = F.add(Set, Sym); - State = State->set(ObjRegion, Set); - C.addTransition(State); - } - return; - } - - if (mayInvalidateBuffer(Call)) { - if (const PtrSet *PS = State->get(ObjRegion)) { - // Mark all pointer symbols associated with the deleted object released. - const Expr *Origin = Call.getOriginExpr(); - for (const auto Symbol : *PS) { - // NOTE: `Origin` may be null, and will be stored so in the symbol's - // `RefState` in MallocChecker's `RegionState` program state map. - State = allocation_state::markReleased(State, Symbol, Origin); - } - State = State->remove(ObjRegion); - C.addTransition(State); - return; - } - } -} - -void DanglingInternalBufferChecker::checkDeadSymbols(SymbolReaper &SymReaper, - CheckerContext &C) const { - ProgramStateRef State = C.getState(); - PtrSet::Factory &F = State->getStateManager().get_context(); - RawPtrMapTy RPM = State->get(); - for (const auto Entry : RPM) { - if (!SymReaper.isLiveRegion(Entry.first)) { - // Due to incomplete destructor support, some dead regions might - // remain in the program state map. Clean them up. - State = State->remove(Entry.first); - } - if (const PtrSet *OldSet = State->get(Entry.first)) { - PtrSet CleanedUpSet = *OldSet; - for (const auto Symbol : Entry.second) { - if (!SymReaper.isLive(Symbol)) - CleanedUpSet = F.remove(CleanedUpSet, Symbol); - } - State = CleanedUpSet.isEmpty() - ? State->remove(Entry.first) - : State->set(Entry.first, CleanedUpSet); - } - } - C.addTransition(State); -} - -std::shared_ptr -DanglingInternalBufferChecker::DanglingBufferBRVisitor::VisitNode( - const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, - BugReport &BR) { - - if (!isSymbolTracked(N->getState(), PtrToBuf) || - isSymbolTracked(PrevN->getState(), PtrToBuf)) - return nullptr; - - const Stmt *S = PathDiagnosticLocation::getStmt(N); - if (!S) - return nullptr; - - SmallString<256> Buf; - llvm::raw_svector_ostream OS(Buf); - OS << "Dangling inner pointer obtained here"; - PathDiagnosticLocation Pos(S, BRC.getSourceManager(), - N->getLocationContext()); - return std::make_shared(Pos, OS.str(), true, - nullptr); -} - -namespace clang { -namespace ento { -namespace allocation_state { - -std::unique_ptr getDanglingBufferBRVisitor(SymbolRef Sym) { - return llvm::make_unique< - DanglingInternalBufferChecker::DanglingBufferBRVisitor>(Sym); -} - -} // end namespace allocation_state -} // end namespace ento -} // end namespace clang - -void ento::registerDanglingInternalBufferChecker(CheckerManager &Mgr) { - registerNewDeleteChecker(Mgr); - Mgr.registerChecker(); -} Index: cfe/trunk/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -0,0 +1,252 @@ +//=== InnerPointerChecker.cpp -------------------------------------*- 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 a check that marks a raw pointer to a C++ container's +// inner buffer released when the object is destroyed. This information can +// be used by MallocChecker to detect use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "AllocationState.h" +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +using PtrSet = llvm::ImmutableSet; + +// Associate container objects with a set of raw pointer symbols. +REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet) + +// This is a trick to gain access to PtrSet's Factory. +namespace clang { +namespace ento { +template <> +struct ProgramStateTrait : public ProgramStatePartialTrait { + static void *GDMIndex() { + static int Index = 0; + return &Index; + } +}; +} // end namespace ento +} // end namespace clang + +namespace { + +class InnerPointerChecker + : public Checker { + + CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, + InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, + ShrinkToFitFn, SwapFn; + +public: + class InnerPointerBRVisitor : public BugReporterVisitor { + SymbolRef PtrToBuf; + + public: + InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} + + static void *getTag() { + static int Tag = 0; + return &Tag; + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } + + std::shared_ptr VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override; + + // FIXME: Scan the map once in the visitor's constructor and do a direct + // lookup by region. + bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { + RawPtrMapTy Map = State->get(); + for (const auto Entry : Map) { + if (Entry.second.contains(Sym)) + return true; + } + return false; + } + }; + + InnerPointerChecker() + : AppendFn("append"), AssignFn("assign"), ClearFn("clear"), + CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"), + PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"), + ReserveFn("reserve"), ResizeFn("resize"), + ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {} + + /// Check whether the function called on the container object is a + /// member function that potentially invalidates pointers referring + /// to the objects's internal buffer. + bool mayInvalidateBuffer(const CallEvent &Call) const; + + /// Record the connection between the symbol returned by c_str() and the + /// corresponding string object region in the ProgramState. Mark the symbol + /// released if the string object is destroyed. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + /// Clean up the ProgramState map. + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// [string.require] +// +// "References, pointers, and iterators referring to the elements of a +// basic_string sequence may be invalidated by the following uses of that +// basic_string object: +// +// -- TODO: As an argument to any standard library function taking a reference +// to non-const basic_string as an argument. For example, as an argument to +// non-member functions swap(), operator>>(), and getline(), or as an argument +// to basic_string::swap(). +// +// -- Calling non-const member functions, except operator[], at, front, back, +// begin, rbegin, end, and rend." +// +bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const { + if (const auto *MemOpCall = dyn_cast(&Call)) { + OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); + if (Opc == OO_Equal || Opc == OO_PlusEqual) + return true; + return false; + } + return (isa(Call) || Call.isCalled(AppendFn) || + Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || + Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || + Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || + Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || + Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || + Call.isCalled(SwapFn)); +} + +void InnerPointerChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *ICall = dyn_cast(&Call); + if (!ICall) + return; + + SVal Obj = ICall->getCXXThisVal(); + const auto *ObjRegion = dyn_cast_or_null(Obj.getAsRegion()); + if (!ObjRegion) + return; + + auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl(); + if (TypeDecl->getName() != "basic_string") + return; + + ProgramStateRef State = C.getState(); + + if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { + SVal RawPtr = Call.getReturnValue(); + if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { + // Start tracking this raw pointer by adding it to the set of symbols + // associated with this container object in the program state map. + PtrSet::Factory &F = State->getStateManager().get_context(); + const PtrSet *SetPtr = State->get(ObjRegion); + PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); + assert(C.wasInlined || !Set.contains(Sym)); + Set = F.add(Set, Sym); + State = State->set(ObjRegion, Set); + C.addTransition(State); + } + return; + } + + if (mayInvalidateBuffer(Call)) { + if (const PtrSet *PS = State->get(ObjRegion)) { + // Mark all pointer symbols associated with the deleted object released. + const Expr *Origin = Call.getOriginExpr(); + for (const auto Symbol : *PS) { + // NOTE: `Origin` may be null, and will be stored so in the symbol's + // `RefState` in MallocChecker's `RegionState` program state map. + State = allocation_state::markReleased(State, Symbol, Origin); + } + State = State->remove(ObjRegion); + C.addTransition(State); + return; + } + } +} + +void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + PtrSet::Factory &F = State->getStateManager().get_context(); + RawPtrMapTy RPM = State->get(); + for (const auto Entry : RPM) { + if (!SymReaper.isLiveRegion(Entry.first)) { + // Due to incomplete destructor support, some dead regions might + // remain in the program state map. Clean them up. + State = State->remove(Entry.first); + } + if (const PtrSet *OldSet = State->get(Entry.first)) { + PtrSet CleanedUpSet = *OldSet; + for (const auto Symbol : Entry.second) { + if (!SymReaper.isLive(Symbol)) + CleanedUpSet = F.remove(CleanedUpSet, Symbol); + } + State = CleanedUpSet.isEmpty() + ? State->remove(Entry.first) + : State->set(Entry.first, CleanedUpSet); + } + } + C.addTransition(State); +} + +std::shared_ptr +InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) { + + if (!isSymbolTracked(N->getState(), PtrToBuf) || + isSymbolTracked(PrevN->getState(), PtrToBuf)) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Dangling inner pointer obtained here"; + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared(Pos, OS.str(), true, + nullptr); +} + +namespace clang { +namespace ento { +namespace allocation_state { + +std::unique_ptr getInnerPointerBRVisitor(SymbolRef Sym) { + return llvm::make_unique(Sym); +} + +} // end namespace allocation_state +} // end namespace ento +} // end namespace clang + +void ento::registerInnerPointerChecker(CheckerManager &Mgr) { + registerNewDeleteChecker(Mgr); + Mgr.registerChecker(); +} Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -47,7 +47,7 @@ AF_CXXNewArray, AF_IfNameIndex, AF_Alloca, - AF_InternalBuffer + AF_InnerBuffer }; class RefState { @@ -485,7 +485,7 @@ (!SPrev || !SPrev->isReleased()); assert(!IsReleased || (Stmt && (isa(Stmt) || isa(Stmt))) || - (!Stmt && S->getAllocationFamily() == AF_InternalBuffer)); + (!Stmt && S->getAllocationFamily() == AF_InnerBuffer)); return IsReleased; } @@ -1473,7 +1473,7 @@ case AF_CXXNew: os << "'new'"; return; case AF_CXXNewArray: os << "'new[]'"; return; case AF_IfNameIndex: os << "'if_nameindex()'"; return; - case AF_InternalBuffer: os << "container-specific allocator"; return; + case AF_InnerBuffer: os << "container-specific allocator"; return; case AF_Alloca: case AF_None: llvm_unreachable("not a deallocation expression"); } @@ -1486,7 +1486,7 @@ case AF_CXXNew: os << "'delete'"; return; case AF_CXXNewArray: os << "'delete[]'"; return; case AF_IfNameIndex: os << "'if_freenameindex()'"; return; - case AF_InternalBuffer: os << "container-specific deallocator"; return; + case AF_InnerBuffer: os << "container-specific deallocator"; return; case AF_Alloca: case AF_None: llvm_unreachable("suspicious argument"); } @@ -1662,8 +1662,8 @@ } case AF_CXXNew: case AF_CXXNewArray: - // FIXME: Add new CheckKind for AF_InternalBuffer. - case AF_InternalBuffer: { + // FIXME: Add new CheckKind for AF_InnerBuffer. + case AF_InnerBuffer: { if (IsALeakCheck) { if (ChecksEnabled[CK_NewDeleteLeaksChecker]) return CK_NewDeleteLeaksChecker; @@ -1995,8 +1995,8 @@ R->addVisitor(llvm::make_unique(Sym)); const RefState *RS = C.getState()->get(Sym); - if (RS->getAllocationFamily() == AF_InternalBuffer) - R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym)); + if (RS->getAllocationFamily() == AF_InnerBuffer) + R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); C.emitReport(std::move(R)); } @@ -2870,7 +2870,7 @@ const Stmt *S = PathDiagnosticLocation::getStmt(N); // When dealing with containers, we sometimes want to give a note // even if the statement is missing. - if (!S && (!RS || RS->getAllocationFamily() != AF_InternalBuffer)) + if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer)) return nullptr; const LocationContext *CurrentLC = N->getLocationContext(); @@ -2903,7 +2903,7 @@ StackHintGeneratorForSymbol *StackHint = nullptr; SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); - + if (Mode == Normal) { if (isAllocated(RS, RSPrev, S)) { Msg = "Memory is allocated"; @@ -2919,7 +2919,7 @@ case AF_IfNameIndex: Msg = "Memory is released"; break; - case AF_InternalBuffer: { + case AF_InnerBuffer: { OS << "Inner pointer invalidated by call to "; if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { OS << "destructor"; @@ -3011,7 +3011,7 @@ // Generate the extra diagnostic. PathDiagnosticLocation Pos; if (!S) { - assert(RS->getAllocationFamily() == AF_InternalBuffer); + assert(RS->getAllocationFamily() == AF_InnerBuffer); auto PostImplCall = N->getLocation().getAs(); if (!PostImplCall) return nullptr; @@ -3055,7 +3055,7 @@ ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) { - AllocationFamily Family = AF_InternalBuffer; + AllocationFamily Family = AF_InnerBuffer; return State->set(Sym, RefState::getReleased(Family, Origin)); } Index: cfe/trunk/test/Analysis/dangling-internal-buffer.cpp =================================================================== --- cfe/trunk/test/Analysis/dangling-internal-buffer.cpp +++ cfe/trunk/test/Analysis/dangling-internal-buffer.cpp @@ -1,291 +0,0 @@ -//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify - -namespace std { - -typedef int size_type; - -template -class basic_string { -public: - basic_string(); - basic_string(const CharT *s); - - ~basic_string(); - void clear(); - - basic_string &operator=(const basic_string &str); - basic_string &operator+=(const basic_string &str); - - const CharT *c_str() const; - const CharT *data() const; - CharT *data(); - - basic_string &append(size_type count, CharT ch); - basic_string &assign(size_type count, CharT ch); - basic_string &erase(size_type index, size_type count); - basic_string &insert(size_type index, size_type count, CharT ch); - basic_string &replace(size_type pos, size_type count, const basic_string &str); - void pop_back(); - void push_back(CharT ch); - void reserve(size_type new_cap); - void resize(size_type count); - void shrink_to_fit(); - void swap(basic_string &other); -}; - -typedef basic_string string; -typedef basic_string wstring; -typedef basic_string u16string; -typedef basic_string u32string; - -} // end namespace std - -void consume(const char *) {} -void consume(const wchar_t *) {} -void consume(const char16_t *) {} -void consume(const char32_t *) {} - -void deref_after_scope_char(bool cond) { - const char *c, *d; - { - std::string s; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - d = s.data(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} - // expected-note@-1 {{Inner pointer invalidated by call to destructor}} - std::string s; - const char *c2 = s.c_str(); - if (cond) { - // expected-note@-1 {{Assuming 'cond' is not equal to 0}} - // expected-note@-2 {{Taking true branch}} - // expected-note@-3 {{Assuming 'cond' is 0}} - // expected-note@-4 {{Taking false branch}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} - } else { - consume(d); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} - } -} - -void deref_after_scope_char_data_non_const() { - char *c; - { - std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} - std::string s; - char *c2 = s.data(); - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_scope_wchar_t(bool cond) { - const wchar_t *c, *d; - { - std::wstring s; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - d = s.data(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} - // expected-note@-1 {{Inner pointer invalidated by call to destructor}} - std::wstring s; - const wchar_t *c2 = s.c_str(); - if (cond) { - // expected-note@-1 {{Assuming 'cond' is not equal to 0}} - // expected-note@-2 {{Taking true branch}} - // expected-note@-3 {{Assuming 'cond' is 0}} - // expected-note@-4 {{Taking false branch}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} - } else { - consume(d); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} - } -} - -void deref_after_scope_char16_t_cstr() { - const char16_t *c16; - { - std::u16string s16; - c16 = s16.c_str(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} - std::u16string s16; - const char16_t *c16_2 = s16.c_str(); - consume(c16); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_scope_char32_t_data() { - const char32_t *c32; - { - std::u32string s32; - c32 = s32.data(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} - std::u32string s32; - const char32_t *c32_2 = s32.data(); - consume(c32); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void multiple_symbols(bool cond) { - const char *c1, *d1; - { - std::string s1; - c1 = s1.c_str(); // expected-note {{Dangling inner pointer obtained here}} - d1 = s1.data(); // expected-note {{Dangling inner pointer obtained here}} - const char *local = s1.c_str(); - consume(local); // no-warning - } // expected-note {{Inner pointer invalidated by call to destructor}} - // expected-note@-1 {{Inner pointer invalidated by call to destructor}} - std::string s2; - const char *c2 = s2.c_str(); - if (cond) { - // expected-note@-1 {{Assuming 'cond' is not equal to 0}} - // expected-note@-2 {{Taking true branch}} - // expected-note@-3 {{Assuming 'cond' is 0}} - // expected-note@-4 {{Taking false branch}} - consume(c1); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} - } else { - consume(d1); // expected-warning {{Use of memory after it is freed}} - } // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_equals() { - const char *c; - std::string s = "hello"; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s = "world"; // expected-note {{Inner pointer invalidated by call to 'operator='}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_plus_equals() { - const char *c; - std::string s = "hello"; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - s += " world"; // expected-note {{Inner pointer invalidated by call to 'operator+='}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_clear() { - const char *c; - std::string s; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.clear(); // expected-note {{Inner pointer invalidated by call to 'clear'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_append() { - const char *c; - std::string s = "hello"; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.append(2, 'x'); // expected-note {{Inner pointer invalidated by call to 'append'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_assign() { - const char *c; - std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - s.assign(4, 'a'); // expected-note {{Inner pointer invalidated by call to 'assign'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_erase() { - const char *c; - std::string s = "hello"; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.erase(0, 2); // expected-note {{Inner pointer invalidated by call to 'erase'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_insert() { - const char *c; - std::string s = "ello"; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.insert(0, 1, 'h'); // expected-note {{Inner pointer invalidated by call to 'insert'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_replace() { - const char *c; - std::string s = "hello world"; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.replace(6, 5, "string"); // expected-note {{Inner pointer invalidated by call to 'replace'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_pop_back() { - const char *c; - std::string s; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.pop_back(); // expected-note {{Inner pointer invalidated by call to 'pop_back'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_push_back() { - const char *c; - std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - s.push_back('c'); // expected-note {{Inner pointer invalidated by call to 'push_back'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_reserve() { - const char *c; - std::string s; - c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} - s.reserve(5); // expected-note {{Inner pointer invalidated by call to 'reserve'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_resize() { - const char *c; - std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - s.resize(5); // expected-note {{Inner pointer invalidated by call to 'resize'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_shrink_to_fit() { - const char *c; - std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - s.shrink_to_fit(); // expected-note {{Inner pointer invalidated by call to 'shrink_to_fit'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_swap() { - const char *c; - std::string s1, s2; - c = s1.data(); // expected-note {{Dangling inner pointer obtained here}} - s1.swap(s2); // expected-note {{Inner pointer invalidated by call to 'swap'}} - consume(c); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} -} - -void deref_after_scope_ok(bool cond) { - const char *c, *d; - std::string s; - { - c = s.c_str(); - d = s.data(); - } - if (cond) - consume(c); // no-warning - else - consume(d); // no-warning -} Index: cfe/trunk/test/Analysis/inner-pointer.cpp =================================================================== --- cfe/trunk/test/Analysis/inner-pointer.cpp +++ cfe/trunk/test/Analysis/inner-pointer.cpp @@ -0,0 +1,291 @@ +//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.InnerPointer %s -analyzer-output=text -verify + +namespace std { + +typedef int size_type; + +template +class basic_string { +public: + basic_string(); + basic_string(const CharT *s); + + ~basic_string(); + void clear(); + + basic_string &operator=(const basic_string &str); + basic_string &operator+=(const basic_string &str); + + const CharT *c_str() const; + const CharT *data() const; + CharT *data(); + + basic_string &append(size_type count, CharT ch); + basic_string &assign(size_type count, CharT ch); + basic_string &erase(size_type index, size_type count); + basic_string &insert(size_type index, size_type count, CharT ch); + basic_string &replace(size_type pos, size_type count, const basic_string &str); + void pop_back(); + void push_back(CharT ch); + void reserve(size_type new_cap); + void resize(size_type count); + void shrink_to_fit(); + void swap(basic_string &other); +}; + +typedef basic_string string; +typedef basic_string wstring; +typedef basic_string u16string; +typedef basic_string u32string; + +} // end namespace std + +void consume(const char *) {} +void consume(const wchar_t *) {} +void consume(const char16_t *) {} +void consume(const char32_t *) {} + +void deref_after_scope_char(bool cond) { + const char *c, *d; + { + std::string s; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + d = s.data(); // expected-note {{Dangling inner pointer obtained here}} + } // expected-note {{Inner pointer invalidated by call to destructor}} + // expected-note@-1 {{Inner pointer invalidated by call to destructor}} + std::string s; + const char *c2 = s.c_str(); + if (cond) { + // expected-note@-1 {{Assuming 'cond' is not equal to 0}} + // expected-note@-2 {{Taking true branch}} + // expected-note@-3 {{Assuming 'cond' is 0}} + // expected-note@-4 {{Taking false branch}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} + } else { + consume(d); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} + } +} + +void deref_after_scope_char_data_non_const() { + char *c; + { + std::string s; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + } // expected-note {{Inner pointer invalidated by call to destructor}} + std::string s; + char *c2 = s.data(); + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_scope_wchar_t(bool cond) { + const wchar_t *c, *d; + { + std::wstring s; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + d = s.data(); // expected-note {{Dangling inner pointer obtained here}} + } // expected-note {{Inner pointer invalidated by call to destructor}} + // expected-note@-1 {{Inner pointer invalidated by call to destructor}} + std::wstring s; + const wchar_t *c2 = s.c_str(); + if (cond) { + // expected-note@-1 {{Assuming 'cond' is not equal to 0}} + // expected-note@-2 {{Taking true branch}} + // expected-note@-3 {{Assuming 'cond' is 0}} + // expected-note@-4 {{Taking false branch}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} + } else { + consume(d); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} + } +} + +void deref_after_scope_char16_t_cstr() { + const char16_t *c16; + { + std::u16string s16; + c16 = s16.c_str(); // expected-note {{Dangling inner pointer obtained here}} + } // expected-note {{Inner pointer invalidated by call to destructor}} + std::u16string s16; + const char16_t *c16_2 = s16.c_str(); + consume(c16); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_scope_char32_t_data() { + const char32_t *c32; + { + std::u32string s32; + c32 = s32.data(); // expected-note {{Dangling inner pointer obtained here}} + } // expected-note {{Inner pointer invalidated by call to destructor}} + std::u32string s32; + const char32_t *c32_2 = s32.data(); + consume(c32); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void multiple_symbols(bool cond) { + const char *c1, *d1; + { + std::string s1; + c1 = s1.c_str(); // expected-note {{Dangling inner pointer obtained here}} + d1 = s1.data(); // expected-note {{Dangling inner pointer obtained here}} + const char *local = s1.c_str(); + consume(local); // no-warning + } // expected-note {{Inner pointer invalidated by call to destructor}} + // expected-note@-1 {{Inner pointer invalidated by call to destructor}} + std::string s2; + const char *c2 = s2.c_str(); + if (cond) { + // expected-note@-1 {{Assuming 'cond' is not equal to 0}} + // expected-note@-2 {{Taking true branch}} + // expected-note@-3 {{Assuming 'cond' is 0}} + // expected-note@-4 {{Taking false branch}} + consume(c1); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} + } else { + consume(d1); // expected-warning {{Use of memory after it is freed}} + } // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_equals() { + const char *c; + std::string s = "hello"; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s = "world"; // expected-note {{Inner pointer invalidated by call to 'operator='}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_plus_equals() { + const char *c; + std::string s = "hello"; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + s += " world"; // expected-note {{Inner pointer invalidated by call to 'operator+='}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_clear() { + const char *c; + std::string s; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.clear(); // expected-note {{Inner pointer invalidated by call to 'clear'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_append() { + const char *c; + std::string s = "hello"; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.append(2, 'x'); // expected-note {{Inner pointer invalidated by call to 'append'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_assign() { + const char *c; + std::string s; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + s.assign(4, 'a'); // expected-note {{Inner pointer invalidated by call to 'assign'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_erase() { + const char *c; + std::string s = "hello"; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.erase(0, 2); // expected-note {{Inner pointer invalidated by call to 'erase'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_insert() { + const char *c; + std::string s = "ello"; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.insert(0, 1, 'h'); // expected-note {{Inner pointer invalidated by call to 'insert'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_replace() { + const char *c; + std::string s = "hello world"; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.replace(6, 5, "string"); // expected-note {{Inner pointer invalidated by call to 'replace'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_pop_back() { + const char *c; + std::string s; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.pop_back(); // expected-note {{Inner pointer invalidated by call to 'pop_back'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_push_back() { + const char *c; + std::string s; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + s.push_back('c'); // expected-note {{Inner pointer invalidated by call to 'push_back'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_reserve() { + const char *c; + std::string s; + c = s.c_str(); // expected-note {{Dangling inner pointer obtained here}} + s.reserve(5); // expected-note {{Inner pointer invalidated by call to 'reserve'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_resize() { + const char *c; + std::string s; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + s.resize(5); // expected-note {{Inner pointer invalidated by call to 'resize'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_shrink_to_fit() { + const char *c; + std::string s; + c = s.data(); // expected-note {{Dangling inner pointer obtained here}} + s.shrink_to_fit(); // expected-note {{Inner pointer invalidated by call to 'shrink_to_fit'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_swap() { + const char *c; + std::string s1, s2; + c = s1.data(); // expected-note {{Dangling inner pointer obtained here}} + s1.swap(s2); // expected-note {{Inner pointer invalidated by call to 'swap'}} + consume(c); // expected-warning {{Use of memory after it is freed}} + // expected-note@-1 {{Use of memory after it is freed}} +} + +void deref_after_scope_ok(bool cond) { + const char *c, *d; + std::string s; + { + c = s.c_str(); + d = s.data(); + } + if (cond) + consume(c); // no-warning + else + consume(d); // no-warning +}