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">,
--- /dev/null
+//===--- AllocationState.h ------------------------------------- *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+
+namespace allocation_state {
+
+ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
+ const Expr *Origin);
+
+} // end namespace allocation_state
+
+} // end namespace ento
+} // end namespace clang
+
+#endif
CloneChecker.cpp
ConversionChecker.cpp
CXXSelfAssignmentChecker.cpp
+ DanglingInternalBufferChecker.cpp
DeadStoresChecker.cpp
DebugCheckers.cpp
DeleteWithNonVirtualDtorChecker.cpp
--- /dev/null
+//=== 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++ standard library
+// container's inner buffer released when the object is destroyed. This
+// information can be used by MallocChecker to detect use-after-free problems.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#include "AllocationState.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class DanglingInternalBufferChecker : public Checker<check::PostCall> {
+ CallDescription CStrFn;
+
+public:
+ DanglingInternalBufferChecker() : CStrFn("c_str") {}
+
+ /// 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;
+};
+
+} // end anonymous namespace
+
+// FIXME: c_str() may be called on a string object many times, so it should
+// have a list of symbols associated with it.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, SymbolRef)
+
+void DanglingInternalBufferChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *ICall = dyn_cast<CXXInstanceCall>(&Call);
+ if (!ICall)
+ return;
+
+ SVal Obj = ICall->getCXXThisVal();
+ const auto *TypedR = dyn_cast_or_null<TypedValueRegion>(Obj.getAsRegion());
+ if (!TypedR)
+ return;
+
+ auto *TypeDecl = TypedR->getValueType()->getAsCXXRecordDecl();
+ if (TypeDecl->getName() != "basic_string")
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (Call.isCalled(CStrFn)) {
+ SVal RawPtr = Call.getReturnValue();
+ if (!RawPtr.isUnknown()) {
+ State = State->set<RawPtrMap>(TypedR, RawPtr.getAsSymbol());
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ if (isa<CXXDestructorCall>(ICall)) {
+ if (State->contains<RawPtrMap>(TypedR)) {
+ const SymbolRef *StrBufferPtr = State->get<RawPtrMap>(TypedR);
+ // FIXME: What if Origin is null?
+ const Expr *Origin = Call.getOriginExpr();
+ State = allocation_state::markReleased(State, *StrBufferPtr, Origin);
+ C.addTransition(State);
+ return;
+ }
+ }
+}
+
+void ento::registerDanglingInternalBufferChecker(CheckerManager &Mgr) {
+ registerNewDeleteChecker(Mgr);
+ Mgr.registerChecker<DanglingInternalBufferChecker>();
+}
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
#include <climits>
#include <utility>
AF_CXXNew,
AF_CXXNewArray,
AF_IfNameIndex,
- AF_Alloca
+ AF_Alloca,
+ AF_InternalBuffer
};
class RefState {
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_Alloca:
case AF_None: llvm_unreachable("not a deallocation expression");
}
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_Alloca:
case AF_None: llvm_unreachable("suspicious argument");
}
return Optional<MallocChecker::CheckKind>();
}
case AF_CXXNew:
- case AF_CXXNewArray: {
+ case AF_CXXNewArray:
+ // FIXME: Add new CheckKind for AF_InternalBuffer.
+ case AF_InternalBuffer: {
if (IsALeakCheck) {
if (ChecksEnabled[CK_NewDeleteLeaksChecker])
return CK_NewDeleteLeaksChecker;
}
}
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+ AllocationFamily Family = AF_InternalBuffer;
+ return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
--- /dev/null
+//RUN: %clang_analyze_cc1 -analyzer-checker=alpha.cplusplus.DanglingInternalBuffer %s -analyzer-output=text -verify
+
+namespace std {
+
+template< typename CharT >
+class basic_string {
+public:
+ ~basic_string();
+ const CharT *c_str();
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+typedef basic_string<char16_t> u16string;
+typedef basic_string<char32_t> 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() {
+ const char *c;
+ {
+ std::string s;
+ c = s.c_str();
+ }
+ 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() {
+ const wchar_t *w;
+ {
+ std::wstring ws;
+ w = ws.c_str();
+ }
+ consume(w); // 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() {
+ const char16_t *c16;
+ {
+ std::u16string s16;
+ c16 = 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() {
+ const char32_t *c32;
+ {
+ std::u32string s32;
+ c32 = s32.c_str();
+ }
+ consume(c32); // expected-warning {{Use of memory after it is freed}}
+ // expected-note@-1 {{Use of memory after it is freed}}
+}
+
+void deref_after_scope_ok() {
+ const char *c;
+ std::string s;
+ {
+ c = s.c_str();
+ }
+ consume(c); // no-warning
+}