Index: lib/StaticAnalyzer/Checkers/AllocationState.h =================================================================== --- lib/StaticAnalyzer/Checkers/AllocationState.h +++ lib/StaticAnalyzer/Checkers/AllocationState.h @@ -26,6 +26,11 @@ /// AF_InternalBuffer symbols. std::unique_ptr getDanglingBufferBRVisitor(SymbolRef Sym); +/// 'Sym' represents a pointer to the inner buffer of a container object. +/// This function looks up the memory region of that object in +/// DanglingInternalBufferChecker's program state map. +const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym); + } // end namespace allocation_state } // end namespace ento Index: lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp +++ lib/StaticAnalyzer/Checkers/DanglingInternalBufferChecker.cpp @@ -212,6 +212,29 @@ C.addTransition(State); } +namespace clang { +namespace ento { +namespace allocation_state { + +std::unique_ptr getDanglingBufferBRVisitor(SymbolRef Sym) { + return llvm::make_unique< + DanglingInternalBufferChecker::DanglingBufferBRVisitor>(Sym); +} + +const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { + RawPtrMapTy Map = State->get(); + for (const auto Entry : Map) { + if (Entry.second.contains(Sym)) { + return Entry.first; + } + } + return nullptr; +} + +} // end namespace allocation_state +} // end namespace ento +} // end namespace clang + std::shared_ptr DanglingInternalBufferChecker::DanglingBufferBRVisitor::VisitNode( const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, @@ -225,28 +248,21 @@ if (!S) return nullptr; + const MemRegion *ObjRegion = + allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); + const auto *TypedRegion = dyn_cast(ObjRegion); + QualType ObjTy = TypedRegion->getValueType(); + SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); - OS << "Dangling inner pointer obtained here"; + OS << "Pointer to inner buffer of '" << ObjTy.getAsString() + << "' object 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: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1987,15 +1987,18 @@ BT_UseFree[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Use-after-free", categories::MemoryError)); + AllocationFamily AF = + C.getState()->get(Sym)->getAllocationFamily(); + auto R = llvm::make_unique(*BT_UseFree[*CheckKind], - "Use of memory after it is freed", N); + AF == AF_InternalBuffer ? "Deallocated pointer returned to the caller" + : "Use of memory after it is freed", N); R->markInteresting(Sym); R->addRange(Range); R->addVisitor(llvm::make_unique(Sym)); - const RefState *RS = C.getState()->get(Sym); - if (RS->getAllocationFamily() == AF_InternalBuffer) + if (AF == AF_InternalBuffer) R->addVisitor(allocation_state::getDanglingBufferBRVisitor(Sym)); C.emitReport(std::move(R)); @@ -2903,7 +2906,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"; @@ -2918,13 +2921,17 @@ case AF_CXXNewArray: case AF_IfNameIndex: Msg = "Memory is released"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; memory was released"); break; case AF_InternalBuffer: { - OS << "Inner pointer invalidated by call to "; + OS << "Inner buffer "; if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { - OS << "destructor"; + OS << "deallocated by call to destructor"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; inner buffer was deallocated"); } else { - OS << "'"; + OS << "reallocated by call to '"; const Stmt *S = RS->getStmt(); if (const auto *MemCallE = dyn_cast(S)) { OS << MemCallE->getMethodDecl()->getNameAsString(); @@ -2932,15 +2939,23 @@ OS << OpCallE->getDirectCallee()->getNameAsString(); } OS << "'"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; inner buffer was reallocated"); } + + // Find the type of the container object. + const MemRegion *ObjRegion = + allocation_state::getContainerObjRegion(statePrev, Sym); + const auto *TypedRegion = dyn_cast(ObjRegion); + QualType ObjTy = TypedRegion->getValueType(); + OS << " on '" << ObjTy.getAsString() << "' object"; + Msg = OS.str(); break; } case AF_None: llvm_unreachable("Unhandled allocation family!"); } - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returning; memory was released"); // See if we're releasing memory while inlining a destructor // (or one of its callees). This turns on various common Index: test/Analysis/dangling-internal-buffer.cpp =================================================================== --- test/Analysis/dangling-internal-buffer.cpp +++ test/Analysis/dangling-internal-buffer.cpp @@ -49,10 +49,10 @@ 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + d = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + } // expected-note {{Inner buffer deallocated by call to destructor}} + // expected-note@-1 {{Inner buffer deallocated by call to destructor}} std::string s; const char *c2 = s.c_str(); if (cond) { @@ -60,11 +60,11 @@ // 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}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } else { - consume(d); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} + consume(d); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } } @@ -72,22 +72,22 @@ char *c; { std::string s; - c = s.data(); // expected-note {{Dangling inner pointer obtained here}} - } // expected-note {{Inner pointer invalidated by call to destructor}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + } // expected-note {{Inner buffer deallocated 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}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::wstring' object obtained here}} + d = s.data(); // expected-note {{Pointer to inner buffer of 'std::wstring' object obtained here}} + } // expected-note {{Inner buffer deallocated by call to destructor}} + // expected-note@-1 {{Inner buffer deallocated by call to destructor}} std::wstring s; const wchar_t *c2 = s.c_str(); if (cond) { @@ -95,11 +95,11 @@ // 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}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } else { - consume(d); // expected-warning {{Use of memory after it is freed}} - // expected-note@-1 {{Use of memory after it is freed}} + consume(d); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } } @@ -107,36 +107,36 @@ 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}} + c16 = s16.c_str(); // expected-note {{Pointer to inner buffer of 'std::u16string' object obtained here}} + } // expected-note {{Inner buffer deallocated 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}} + consume(c16); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c32 = s32.data(); // expected-note {{Pointer to inner buffer of 'std::u32string' object obtained here}} + } // expected-note {{Inner buffer deallocated 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}} + consume(c32); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c1 = s1.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + d1 = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' object 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}} + } // expected-note {{Inner buffer deallocated by call to destructor}} + // expected-note@-1 {{Inner buffer deallocated by call to destructor}} std::string s2; const char *c2 = s2.c_str(); if (cond) { @@ -144,137 +144,169 @@ // 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}} + consume(c1); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } else { - consume(d1); // expected-warning {{Use of memory after it is freed}} - } // expected-note@-1 {{Use of memory after it is freed}} + consume(d1); // expected-warning {{Deallocated pointer returned to the caller}} + } // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s = "world"; // expected-note {{Inner buffer reallocated by call to 'operator='}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s += " world"; // expected-note {{Inner buffer reallocated by call to 'operator+='}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.clear(); // expected-note {{Inner buffer reallocated by call to 'clear'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.append(2, 'x'); // expected-note {{Inner buffer reallocated by call to 'append'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.assign(4, 'a'); // expected-note {{Inner buffer reallocated by call to 'assign'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.erase(0, 2); // expected-note {{Inner buffer reallocated by call to 'erase'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.insert(0, 1, 'h'); // expected-note {{Inner buffer reallocated by call to 'insert'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.replace(6, 5, "string"); // expected-note {{Inner buffer reallocated by call to 'replace'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.pop_back(); // expected-note {{Inner buffer reallocated by call to 'pop_back'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.push_back('c'); // expected-note {{Inner buffer reallocated by call to 'push_back'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.reserve(5); // expected-note {{Inner buffer reallocated by call to 'reserve'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.resize(5); // expected-note {{Inner buffer reallocated by call to 'resize'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s.shrink_to_fit(); // expected-note {{Inner buffer reallocated by call to 'shrink_to_fit'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } 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}} + c = s1.data(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + s1.swap(s2); // expected-note {{Inner buffer reallocated by call to 'swap'}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} +} + +struct S { + std::string s; + const char *name() { + return s.c_str(); // expected-note {{Pointer to inner buffer of 'std::string' object obtained here}} + // expected-note@-1 {{Pointer to inner buffer of 'std::string' object obtained here}} + } + void clear() { + s.clear(); // expected-note {{Inner buffer reallocated by call to 'clear'}} + } + ~S() {} // expected-note {{Inner buffer deallocated by call to destructor on 'std::string' object}} +}; + +void cleared_through_method() { + S x; + const char *c = x.name(); // expected-note {{Calling 'S::name'}} + // expected-note@-1 {{Returning from 'S::name'}} + x.clear(); // expected-note {{Calling 'S::clear'}} + // expected-note@-1 {{Returning; inner buffer was reallocated}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} +} + +void destroyed_through_method() { + S y; + const char *c = y.name(); // expected-note {{Calling 'S::name'}} + // expected-note@-1 {{Returning from 'S::name'}} + y.~S(); // expected-note {{Calling '~S'}} + // expected-note@-1 {{Returning; inner buffer was deallocated}} + consume(c); // expected-warning {{Deallocated pointer returned to the caller}} + // expected-note@-1 {{Deallocated pointer returned to the caller}} } void deref_after_scope_ok(bool cond) {