From e3ba09890ac9769c514f419f80566ee612103b89 Mon Sep 17 00:00:00 2001 From: Adam Balogh Date: Thu, 28 Jun 2018 07:35:23 +0000 Subject: [PATCH] [Analyzer] Constraint Manager Negates Difference If range [m .. n] is stored for symbolic expression A - B, then we can deduce the range for B - A which is [-n .. -m]. This is only true for signed types, unless the range is [0 .. 0]. Differential Revision: https://reviews.llvm.org/D35110 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@335814 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../PathSensitive/RangedConstraintManager.h | 2 + .../Core/RangeConstraintManager.cpp | 69 ++++++++++++- .../constraint_manager_negate_difference.c | 98 +++++++++++++++++++ test/Analysis/ptr-arith.c | 23 ----- 4 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 test/Analysis/constraint_manager_negate_difference.c diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h index 1147466d70..d2ba1f7c95 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -115,6 +115,8 @@ public: RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower, llvm::APSInt Upper) const; + RangeSet Negate(BasicValueFactory &BV, Factory &F) const; + void print(raw_ostream &os) const; bool operator==(const RangeSet &other) const { diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index c081a65002..1d2b94d6f7 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -174,6 +174,38 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, return newRanges; } +// Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set +// [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal +// signed values of the type. +RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = begin(), e = end(); i != e; ++i) { + const llvm::APSInt &from = i->From(), &to = i->To(); + const llvm::APSInt &newTo = (from.isMinSignedValue() ? + BV.getMaxValue(from) : + BV.getValue(- from)); + if (to.isMaxSignedValue() && !newRanges.isEmpty() && + newRanges.begin()->From().isMinSignedValue()) { + assert(newRanges.begin()->To().isMinSignedValue() && + "Ranges should not overlap"); + assert(!from.isMinSignedValue() && "Ranges should not overlap"); + const llvm::APSInt &newFrom = newRanges.begin()->From(); + newRanges = + F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo)); + } else if (!to.isMinSignedValue()) { + const llvm::APSInt &newFrom = BV.getValue(- to); + newRanges = F.add(newRanges, Range(newFrom, newTo)); + } + if (from.isMinSignedValue()) { + newRanges = F.add(newRanges, Range(BV.getMinValue(from), + BV.getMinValue(from))); + } + } + + return newRanges; +} + void RangeSet::print(raw_ostream &os) const { bool isFirst = true; os << "{ "; @@ -252,6 +284,8 @@ private: RangeSet::Factory F; RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + const RangeSet* getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym); RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, @@ -268,6 +302,7 @@ private: RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, const llvm::APSInt &Adjustment); + }; } // end anonymous namespace @@ -423,9 +458,15 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, if (ConstraintRangeTy::data_type *V = State->get(Sym)) return *V; + BasicValueFactory &BV = getBasicVals(); + + // If Sym is a difference of symbols A - B, then maybe we have range set + // stored for B - A. + if (const RangeSet *R = getRangeForMinusSymbol(State, Sym)) + return R->Negate(BV, F); + // Lazily generate a new RangeSet representing all possible values for the // given symbol type. - BasicValueFactory &BV = getBasicVals(); QualType T = Sym->getType(); RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T)); @@ -441,6 +482,32 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, return Result; } +// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to +// obtain the negated symbolic expression instead of constructing the +// symbol manually. This will allow us to support finding ranges of not +// only negated SymSymExpr-type expressions, but also of other, simpler +// expressions which we currently do not know how to negate. +const RangeSet* +RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym) { + if (const SymSymExpr *SSE = dyn_cast(Sym)) { + if (SSE->getOpcode() == BO_Sub) { + QualType T = Sym->getType(); + SymbolManager &SymMgr = State->getSymbolManager(); + SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, + SSE->getLHS(), T); + if (const RangeSet *negV = State->get(negSym)) { + // Unsigned range set cannot be negated, unless it is [0, 0]. + if ((negV->getConcreteValue() && + (*negV->getConcreteValue() == 0)) || + T->isSignedIntegerOrEnumerationType()) + return negV; + } + } + } + return nullptr; +} + //===------------------------------------------------------------------------=== // assumeSymX methods: protected interface for RangeConstraintManager. //===------------------------------------------------------------------------===/ diff --git a/test/Analysis/constraint_manager_negate_difference.c b/test/Analysis/constraint_manager_negate_difference.c new file mode 100644 index 0000000000..6856f48bb0 --- /dev/null +++ b/test/Analysis/constraint_manager_negate_difference.c @@ -0,0 +1,98 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection,core.builtin -analyzer-config aggressive-relational-comparison-simplification=true -verify %s + +void clang_analyzer_eval(int); + +void exit(int); + +#define UINT_MAX (~0U) +#define INT_MAX (UINT_MAX & (UINT_MAX >> 1)) +#define INT_MIN (UINT_MAX & ~(UINT_MAX >> 1)) + +extern void __assert_fail (__const char *__assertion, __const char *__file, + unsigned int __line, __const char *__function) + __attribute__ ((__noreturn__)); +#define assert(expr) \ + ((expr) ? (void)(0) : __assert_fail (#expr, __FILE__, __LINE__, __func__)) + +void assert_in_range(int x) { + assert(x <= ((int)INT_MAX / 4)); + assert(x >= -(((int)INT_MAX) / 4)); +} + +void assert_in_wide_range(int x) { + assert(x <= ((int)INT_MAX / 2)); + assert(x >= -(((int)INT_MAX) / 2)); +} + +void assert_in_range_2(int m, int n) { + assert_in_range(m); + assert_in_range(n); +} + +void equal(int m, int n) { + assert_in_range_2(m, n); + if (m != n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n == m); // expected-warning{{TRUE}} +} + +void non_equal(int m, int n) { + assert_in_range_2(m, n); + if (m == n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n != m); // expected-warning{{TRUE}} +} + +void less_or_equal(int m, int n) { + assert_in_range_2(m, n); + if (m < n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n <= m); // expected-warning{{TRUE}} +} + +void less(int m, int n) { + assert_in_range_2(m, n); + if (m <= n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n < m); // expected-warning{{TRUE}} +} + +void greater_or_equal(int m, int n) { + assert_in_range_2(m, n); + if (m > n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n >= m); // expected-warning{{TRUE}} +} + +void greater(int m, int n) { + assert_in_range_2(m, n); + if (m >= n) + return; + assert_in_wide_range(m - n); + clang_analyzer_eval(n > m); // expected-warning{{TRUE}} +} + +void negate_positive_range(int m, int n) { + if (m - n <= 0) + return; + clang_analyzer_eval(n - m < 0); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m > INT_MIN); // expected-warning{{TRUE}} + clang_analyzer_eval(n - m == INT_MIN); // expected-warning{{FALSE}} +} + +void negate_int_min(int m, int n) { + if (m - n != INT_MIN) + return; + clang_analyzer_eval(n - m == INT_MIN); // expected-warning{{TRUE}} +} + +void negate_mixed(int m, int n) { + if (m -n > INT_MIN && m - n <= 0) + return; + clang_analyzer_eval(n - m <= 0); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/ptr-arith.c b/test/Analysis/ptr-arith.c index 804759a32d..c0b20508d8 100644 --- a/test/Analysis/ptr-arith.c +++ b/test/Analysis/ptr-arith.c @@ -265,49 +265,26 @@ void size_implies_comparison(int *lhs, int *rhs) { clang_analyzer_eval((rhs - lhs) > 0); // expected-warning{{TRUE}} } -//------------------------------- -// False positives -//------------------------------- - void zero_implies_reversed_equal(int *lhs, int *rhs) { clang_analyzer_eval((rhs - lhs) == 0); // expected-warning{{UNKNOWN}} if ((rhs - lhs) == 0) { -#ifdef ANALYZER_CM_Z3 clang_analyzer_eval(rhs != lhs); // expected-warning{{FALSE}} clang_analyzer_eval(rhs == lhs); // expected-warning{{TRUE}} -#else - clang_analyzer_eval(rhs != lhs); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(rhs == lhs); // expected-warning{{UNKNOWN}} -#endif return; } clang_analyzer_eval((rhs - lhs) == 0); // expected-warning{{FALSE}} -#ifdef ANALYZER_CM_Z3 clang_analyzer_eval(rhs == lhs); // expected-warning{{FALSE}} clang_analyzer_eval(rhs != lhs); // expected-warning{{TRUE}} -#else - clang_analyzer_eval(rhs == lhs); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(rhs != lhs); // expected-warning{{UNKNOWN}} -#endif } void canonical_equal(int *lhs, int *rhs) { clang_analyzer_eval(lhs == rhs); // expected-warning{{UNKNOWN}} if (lhs == rhs) { -#ifdef ANALYZER_CM_Z3 clang_analyzer_eval(rhs == lhs); // expected-warning{{TRUE}} -#else - clang_analyzer_eval(rhs == lhs); // expected-warning{{UNKNOWN}} -#endif return; } clang_analyzer_eval(lhs == rhs); // expected-warning{{FALSE}} - -#ifdef ANALYZER_CM_Z3 clang_analyzer_eval(rhs == lhs); // expected-warning{{FALSE}} -#else - clang_analyzer_eval(rhs == lhs); // expected-warning{{UNKNOWN}} -#endif } void compare_element_region_and_base(int *p) { -- 2.40.0