From 8c0250d7e9aa7dc86f27b0d2820043d6932ea0b1 Mon Sep 17 00:00:00 2001 From: Pierre Gousseau Date: Tue, 12 Jan 2016 10:07:56 +0000 Subject: [PATCH] [analyzer] Evaluate integral casts as cast symbols if truncations are detected. The current workaround for truncations not being modelled is that the evaluation of integer to integer casts are simply bypassed and so the original symbol is used as the new casted symbol (cf SimpleSValBuilder::evalCastFromNonLoc). This lead to the issue described in PR25078, as the RangeConstraintManager associates ranges with symbols. The new evalIntegralCast method added by this patch wont bypass the cast if it finds the range of the symbol to be greater than the maximum value of the target type. The fix to RangeSet::pin mentioned in the initial review will be committed separately. Differential Revision: http://reviews.llvm.org/D12901 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@257464 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/PathSensitive/SValBuilder.h | 6 +- lib/StaticAnalyzer/Core/ExprEngineC.cpp | 9 +- lib/StaticAnalyzer/Core/SValBuilder.cpp | 39 ++++++ test/Analysis/range_casts.c | 126 ++++++++++++++++++ 4 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 test/Analysis/range_casts.c diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index a68d3410a8..3c47114e2d 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -83,7 +83,11 @@ public: } SVal evalCast(SVal val, QualType castTy, QualType originalType); - + + // Handles casts of type CK_IntegralCast. + SVal evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, + QualType originalType); + virtual SVal evalMinus(NonLoc val) = 0; virtual SVal evalComplement(NonLoc val) = 0; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index a5b58710b2..3a30b29781 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -316,7 +316,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_ArrayToPointerDecay: case CK_BitCast: case CK_AddressSpaceConversion: - case CK_IntegralCast: case CK_NullToPointer: case CK_IntegralToPointer: case CK_PointerToIntegral: @@ -349,6 +348,14 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_IntegralCast: { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, LCtx); + V = svalBuilder.evalIntegralCast(state, V, T, ExTy); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } case CK_DerivedToBase: case CK_UncheckedDerivedToBase: { // For DerivedToBase cast, delegate to the store manager. diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index cdae04068e..d74e353951 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -423,6 +423,45 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, return true; } +// Handles casts of type CK_IntegralCast. +// At the moment, this function will redirect to evalCast, except when the range +// of the original value is known to be greater than the max of the target type. +SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, + QualType castTy, QualType originalTy) { + + // No truncations if target type is big enough. + if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) + return evalCast(val, castTy, originalTy); + + const SymExpr *se = val.getAsSymbolicExpression(); + if (!se) // Let evalCast handle non symbolic expressions. + return evalCast(val, castTy, originalTy); + + // Find the maximum value of the target type. + APSIntType ToType(getContext().getTypeSize(castTy), + castTy->isUnsignedIntegerType()); + llvm::APSInt ToTypeMax = ToType.getMaxValue(); + NonLoc ToTypeMaxVal = + makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() + : ToTypeMax.getSExtValue(), + castTy) + .castAs(); + // Check the range of the symbol being casted against the maximum value of the + // target type. + NonLoc FromVal = val.castAs(); + QualType CmpTy = getConditionType(); + NonLoc CompVal = + evalBinOpNN(state, BO_LT, FromVal, ToTypeMaxVal, CmpTy).castAs(); + ProgramStateRef IsNotTruncated, IsTruncated; + std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); + if (!IsNotTruncated && IsTruncated) { + // Symbol is truncated so we evaluate it as a cast. + NonLoc CastVal = makeNonLoc(se, originalTy, castTy); + return CastVal; + } + return evalCast(val, castTy, originalTy); +} + // FIXME: should rewrite according to the cast kind. SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { castTy = Context.getCanonicalType(castTy); diff --git a/test/Analysis/range_casts.c b/test/Analysis/range_casts.c new file mode 100644 index 0000000000..f056cb85a3 --- /dev/null +++ b/test/Analysis/range_casts.c @@ -0,0 +1,126 @@ +// This test checks that intersecting ranges does not cause 'system is over constrained' assertions in the case of eg: 32 bits unsigned integers getting their range from 64 bits signed integers. +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s + +void clang_analyzer_warnIfReached(); + +void f1(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) // because of foo range, index is in range [0; UINT_MAX] + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f2(unsigned long foo) +{ + int index = -1; + if (index < foo) index = foo; // index equals ULONG_MAX + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning +} + +void f3(unsigned long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f4(long foo) +{ + int index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f5(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index == -1) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f6(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index == -1) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f7(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1 == 0) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f9(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1L == 0L) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f10(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0L) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f12(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1UL == 0L) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f14(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + long bar = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f15(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + unsigned int tmp = index + 1; + if (tmp == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} -- 2.40.0