r260043 - [analyzer] Invalidate destination of std::copy() and std::copy_backward().
Devin Coughlin via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 7 08:55:45 PST 2016
Author: dcoughlin
Date: Sun Feb 7 10:55:44 2016
New Revision: 260043
URL: http://llvm.org/viewvc/llvm-project?rev=260043&view=rev
Log:
[analyzer] Invalidate destination of std::copy() and std::copy_backward().
Now that the libcpp implementations of these methods has a branch that doesn't call
memmove(), the analyzer needs to invalidate the destination for these methods explicitly.
rdar://problem/23575656
Added:
cfe/trunk/test/Analysis/bstring.cpp
Modified:
cfe/trunk/include/clang/Analysis/AnalysisContext.h
cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
cfe/trunk/test/Analysis/Inputs/system-header-simulator-cxx.h
cfe/trunk/test/Analysis/diagnostics/explicit-suppression.cpp
Modified: cfe/trunk/include/clang/Analysis/AnalysisContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/AnalysisContext.h?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/AnalysisContext.h (original)
+++ cfe/trunk/include/clang/Analysis/AnalysisContext.h Sun Feb 7 10:55:44 2016
@@ -201,6 +201,10 @@ public:
}
return static_cast<T*>(data);
}
+
+ /// Returns true if the root namespace of the given declaration is the 'std'
+ /// C++ namespace.
+ static bool isInStdNamespace(const Decl *D);
private:
ManagedAnalysis *&getAnalysisImpl(const void* tag);
Modified: cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp (original)
+++ cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp Sun Feb 7 10:55:44 2016
@@ -317,6 +317,21 @@ AnalysisDeclContext::getBlockInvocationC
BD, ContextData);
}
+bool AnalysisDeclContext::isInStdNamespace(const Decl *D) {
+ const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext();
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
+ if (!ND)
+ return false;
+
+ while (const DeclContext *Parent = ND->getParent()) {
+ if (!isa<NamespaceDecl>(Parent))
+ break;
+ ND = cast<NamespaceDecl>(Parent);
+ }
+
+ return ND->isStdNamespace();
+}
+
LocationContextManager & AnalysisDeclContext::getLocationContextManager() {
assert(Manager &&
"Cannot create LocationContexts without an AnalysisDeclContextManager!");
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp Sun Feb 7 10:55:44 2016
@@ -118,6 +118,10 @@ public:
void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const;
+
// Utility methods
std::pair<ProgramStateRef , ProgramStateRef >
static assumeZero(CheckerContext &C,
@@ -1950,7 +1954,57 @@ void CStringChecker::evalStrsep(CheckerC
C.addTransition(State);
}
+// These should probably be moved into a C++ standard library checker.
+void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const {
+ evalStdCopyCommon(C, CE);
+}
+
+void CStringChecker::evalStdCopyBackward(CheckerContext &C,
+ const CallExpr *CE) const {
+ evalStdCopyCommon(C, CE);
+}
+
+void CStringChecker::evalStdCopyCommon(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+ ProgramStateRef State = C.getState();
+
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // template <class _InputIterator, class _OutputIterator>
+ // _OutputIterator
+ // copy(_InputIterator __first, _InputIterator __last,
+ // _OutputIterator __result)
+
+ // Invalidate the destination buffer
+ const Expr *Dst = CE->getArg(2);
+ SVal DstVal = State->getSVal(Dst, LCtx);
+ State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false,
+ /*Size=*/nullptr);
+
+ SValBuilder &SVB = C.getSValBuilder();
+
+ SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ State = State->BindExpr(CE, LCtx, ResultVal);
+
+ C.addTransition(State);
+}
+
+static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) {
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return false;
+
+ if (!AnalysisDeclContext::isInStdNamespace(FD))
+ return false;
+
+ if (II->getName().equals(Name))
+ return true;
+
+ return false;
+}
//===----------------------------------------------------------------------===//
// The driver method, and other Checker callbacks.
//===----------------------------------------------------------------------===//
@@ -1999,6 +2053,10 @@ bool CStringChecker::evalCall(const Call
evalFunction = &CStringChecker::evalBcopy;
else if (C.isCLibraryFunction(FDecl, "bcmp"))
evalFunction = &CStringChecker::evalMemcmp;
+ else if (isCPPStdLibraryFunction(FDecl, "copy"))
+ evalFunction = &CStringChecker::evalStdCopy;
+ else if (isCPPStdLibraryFunction(FDecl, "copy_backward"))
+ evalFunction = &CStringChecker::evalStdCopyBackward;
// If the callee isn't a string function, let another checker handle it.
if (!evalFunction)
Modified: cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp Sun Feb 7 10:55:44 2016
@@ -1540,20 +1540,6 @@ ConditionBRVisitor::VisitTrueTest(const
return event;
}
-
-// FIXME: Copied from ExprEngineCallAndReturn.cpp.
-static bool isInStdNamespace(const Decl *D) {
- const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext();
- const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
- if (!ND)
- return false;
-
- while (const NamespaceDecl *Parent = dyn_cast<NamespaceDecl>(ND->getParent()))
- ND = Parent;
-
- return ND->isStdNamespace();
-}
-
std::unique_ptr<PathDiagnosticPiece>
LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
@@ -1564,7 +1550,7 @@ LikelyFalsePositiveSuppressionBRVisitor:
AnalyzerOptions &Options = Eng.getAnalysisManager().options;
const Decl *D = N->getLocationContext()->getDecl();
- if (isInStdNamespace(D)) {
+ if (AnalysisDeclContext::isInStdNamespace(D)) {
// Skip reports within the 'std' namespace. Although these can sometimes be
// the user's fault, we currently don't report them very well, and
// Note that this will not help for any other data structure libraries, like
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Sun Feb 7 10:55:44 2016
@@ -382,21 +382,6 @@ void ExprEngine::examineStackFrames(cons
}
-static bool IsInStdNamespace(const FunctionDecl *FD) {
- const DeclContext *DC = FD->getEnclosingNamespaceContext();
- const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
- if (!ND)
- return false;
-
- while (const DeclContext *Parent = ND->getParent()) {
- if (!isa<NamespaceDecl>(Parent))
- break;
- ND = cast<NamespaceDecl>(Parent);
- }
-
- return ND->isStdNamespace();
-}
-
// The GDM component containing the dynamic dispatch bifurcation info. When
// the exact type of the receiver is not known, we want to explore both paths -
// one on which we do inline it and the other one on which we don't. This is
@@ -761,7 +746,7 @@ static bool mayInlineDecl(AnalysisDeclCo
// Conditionally control the inlining of C++ standard library functions.
if (!Opts.mayInlineCXXStandardLibrary())
if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation()))
- if (IsInStdNamespace(FD))
+ if (AnalysisDeclContext::isInStdNamespace(FD))
return false;
// Conditionally control the inlining of methods on objects that look
Modified: cfe/trunk/test/Analysis/Inputs/system-header-simulator-cxx.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Inputs/system-header-simulator-cxx.h?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/Inputs/system-header-simulator-cxx.h (original)
+++ cfe/trunk/test/Analysis/Inputs/system-header-simulator-cxx.h Sun Feb 7 10:55:44 2016
@@ -7,6 +7,9 @@
typedef unsigned char uint8_t;
+typedef __typeof__(sizeof(int)) size_t;
+void *memmove(void *s1, const void *s2, size_t n);
+
namespace std {
template <class T1, class T2>
struct pair {
@@ -104,11 +107,120 @@ namespace std {
const _E* end() const {return __begin_ + __size_;}
};
+ template <bool, class _Tp = void> struct enable_if {};
+ template <class _Tp> struct enable_if<true, _Tp> {typedef _Tp type;};
+
+ template <class _Tp, _Tp __v>
+ struct integral_constant
+ {
+ static const _Tp value = __v;
+ typedef _Tp value_type;
+ typedef integral_constant type;
+
+ operator value_type() const {return value;}
+
+ value_type operator ()() const {return value;}
+ };
+
+ template <class _Tp, _Tp __v>
+ const _Tp integral_constant<_Tp, __v>::value;
+
+ template <class _Tp, class _Arg>
+ struct is_trivially_assignable
+ : integral_constant<bool, __is_trivially_assignable(_Tp, _Arg)>
+ {
+ };
+
+ typedef integral_constant<bool,true> true_type;
+ typedef integral_constant<bool,false> false_type;
+
+ template <class _Tp> struct is_const : public false_type {};
+ template <class _Tp> struct is_const<_Tp const> : public true_type {};
+
+ template <class _Tp> struct is_reference : public false_type {};
+ template <class _Tp> struct is_reference<_Tp&> : public true_type {};
+
+ template <class _Tp, class _Up> struct is_same : public false_type {};
+ template <class _Tp> struct is_same<_Tp, _Tp> : public true_type {};
+
+ template <class _Tp, bool = is_const<_Tp>::value || is_reference<_Tp>::value >
+ struct __add_const {typedef _Tp type;};
+
+ template <class _Tp>
+ struct __add_const<_Tp, false> {typedef const _Tp type;};
+
+ template <class _Tp> struct add_const {typedef typename __add_const<_Tp>::type type;};
+
+ template <class _Tp> struct remove_const {typedef _Tp type;};
+ template <class _Tp> struct remove_const<const _Tp> {typedef _Tp type;};
+
+ template <class _Tp> struct add_lvalue_reference {typedef _Tp& type;};
+
+ template <class _Tp> struct is_trivially_copy_assignable
+ : public is_trivially_assignable<typename add_lvalue_reference<_Tp>::type,
+ typename add_lvalue_reference<typename add_const<_Tp>::type>::type> {};
+
+ template<class InputIter, class OutputIter>
+ OutputIter __copy(InputIter II, InputIter IE, OutputIter OI) {
+ while (II != IE)
+ *OI++ = *II++;
+
+ return OI;
+ }
+
+ template <class _Tp, class _Up>
+ inline
+ typename enable_if
+ <
+ is_same<typename remove_const<_Tp>::type, _Up>::value &&
+ is_trivially_copy_assignable<_Up>::value,
+ _Up*
+ >::type __copy(_Tp* __first, _Tp* __last, _Up* __result) {
+ size_t __n = __last - __first;
+
+ if (__n > 0)
+ memmove(__result, __first, __n * sizeof(_Up));
+
+ return __result + __n;
+ }
+
template<class InputIter, class OutputIter>
OutputIter copy(InputIter II, InputIter IE, OutputIter OI) {
- while (II != IE)
- *OI++ = *II++;
- return OI;
+ return __copy(II, IE, OI);
+ }
+
+ template <class _BidirectionalIterator, class _OutputIterator>
+ inline
+ _OutputIterator
+ __copy_backward(_BidirectionalIterator __first, _BidirectionalIterator __last,
+ _OutputIterator __result)
+ {
+ while (__first != __last)
+ *--__result = *--__last;
+ return __result;
+ }
+
+ template <class _Tp, class _Up>
+ inline
+ typename enable_if
+ <
+ is_same<typename remove_const<_Tp>::type, _Up>::value &&
+ is_trivially_copy_assignable<_Up>::value,
+ _Up*
+ >::type __copy_backward(_Tp* __first, _Tp* __last, _Up* __result) {
+ size_t __n = __last - __first;
+
+ if (__n > 0)
+ {
+ __result -= __n;
+ memmove(__result, __first, __n * sizeof(_Up));
+ }
+ return __result;
+ }
+
+ template<class InputIter, class OutputIter>
+ OutputIter copy_backward(InputIter II, InputIter IE, OutputIter OI) {
+ return __copy_backward(II, IE, OI);
}
struct input_iterator_tag { };
Added: cfe/trunk/test/Analysis/bstring.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/bstring.cpp?rev=260043&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/bstring.cpp (added)
+++ cfe/trunk/test/Analysis/bstring.cpp Sun Feb 7 10:55:44 2016
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+#include "Inputs/system-header-simulator-for-malloc.h"
+
+void clang_analyzer_eval(int);
+
+int *testStdCopyInvalidatesBuffer(std::vector<int> v) {
+ int n = v.size();
+ int *buf = (int *)malloc(n * sizeof(int));
+
+ buf[0] = 66;
+
+ // Call to copy should invalidate buf.
+ std::copy(v.begin(), v.end(), buf);
+
+ int i = buf[0];
+
+ clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}}
+
+ return buf;
+}
+
+int *testStdCopyBackwardInvalidatesBuffer(std::vector<int> v) {
+ int n = v.size();
+ int *buf = (int *)malloc(n * sizeof(int));
+
+ buf[0] = 66;
+
+ // Call to copy_backward should invalidate buf.
+ std::copy_backward(v.begin(), v.end(), buf + n);
+
+ int i = buf[0];
+
+ clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}}
+
+ return buf;
+}
Modified: cfe/trunk/test/Analysis/diagnostics/explicit-suppression.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/diagnostics/explicit-suppression.cpp?rev=260043&r1=260042&r2=260043&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/diagnostics/explicit-suppression.cpp (original)
+++ cfe/trunk/test/Analysis/diagnostics/explicit-suppression.cpp Sun Feb 7 10:55:44 2016
@@ -9,9 +9,15 @@
void clang_analyzer_eval(bool);
-void testCopyNull(int *I, int *E) {
- std::copy(I, E, (int *)0);
+class C {
+ // The virtual function is to make C not trivially copy assignable so that we call the
+ // variant of std::copy() that does not defer to memmove().
+ virtual int f();
+};
+
+void testCopyNull(C *I, C *E) {
+ std::copy(I, E, (C *)0);
#ifndef SUPPRESSED
- // expected-warning at ../Inputs/system-header-simulator-cxx.h:110 {{Dereference of null pointer}}
+ // expected-warning at ../Inputs/system-header-simulator-cxx.h:166 {{Called C++ object pointer is null}}
#endif
}
More information about the cfe-commits
mailing list