diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-04 19:20:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-02-08 19:02:26 +0000 |
commit | 81ad626541db97eb356e2c1d4a20eb2a26a766ab (patch) | |
tree | 311b6a8987c32b1e1dcbab65c54cfac3fdb56175 /contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | |
parent | 5fff09660e06a66bed6482da9c70df328e16bbb6 (diff) | |
parent | 145449b1e420787bb99721a429341fa6be3adfb6 (diff) |
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp | 173 |
1 files changed, 121 insertions, 52 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 0bd47ced15a5..762ecc18ecea 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -22,6 +22,13 @@ using namespace ento; namespace { class SimpleSValBuilder : public SValBuilder { + // Query the constraint manager whether the SVal has only one possible + // (integer) value. If that is the case, the value is returned. Otherwise, + // returns NULL. + // This is an implementation detail. Checkers should use `getKnownValue()` + // instead. + const llvm::APSInt *getConstValue(ProgramStateRef state, SVal V); + // With one `simplifySValOnce` call, a compound symbols might collapse to // simpler symbol tree that is still possible to further simplify. Thus, we // do the simplification on a new symbol tree until we reach the simplest @@ -45,7 +52,7 @@ class SimpleSValBuilder : public SValBuilder { SVal simplifyUntilFixpoint(ProgramStateRef State, SVal Val); // Recursively descends into symbolic expressions and replaces symbols - // with their known values (in the sense of the getKnownValue() method). + // with their known values (in the sense of the getConstValue() method). // We traverse the symbol tree and query the constraint values for the // sub-trees and if a value is a constant we do the constant folding. SVal simplifySValOnce(ProgramStateRef State, SVal V); @@ -56,8 +63,6 @@ public: : SValBuilder(alloc, context, stateMgr) {} ~SimpleSValBuilder() override {} - SVal evalMinus(NonLoc val) override; - SVal evalComplement(NonLoc val) override; SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy) override; SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, @@ -65,8 +70,9 @@ public: SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) override; - /// getKnownValue - evaluates a given SVal. If the SVal has only one possible - /// (integer) value, that value is returned. Otherwise, returns NULL. + /// Evaluates a given SVal by recursively evaluating and + /// simplifying the children SVals. If the SVal has only one possible + /// (integer) value, that value is returned. Otherwise, returns NULL. const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; SVal simplifySVal(ProgramStateRef State, SVal V) override; @@ -82,26 +88,21 @@ SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, return new SimpleSValBuilder(alloc, context, stateMgr); } -//===----------------------------------------------------------------------===// -// Transfer function for unary operators. -//===----------------------------------------------------------------------===// - -SVal SimpleSValBuilder::evalMinus(NonLoc val) { - switch (val.getSubKind()) { - case nonloc::ConcreteIntKind: - return val.castAs<nonloc::ConcreteInt>().evalMinus(*this); - default: - return UnknownVal(); +// Checks if the negation the value and flipping sign preserve +// the semantics on the operation in the resultType +static bool isNegationValuePreserving(const llvm::APSInt &Value, + APSIntType ResultType) { + const unsigned ValueBits = Value.getSignificantBits(); + if (ValueBits == ResultType.getBitWidth()) { + // The value is the lowest negative value that is representable + // in signed integer with bitWith of result type. The + // negation is representable if resultType is unsigned. + return ResultType.isUnsigned(); } -} -SVal SimpleSValBuilder::evalComplement(NonLoc X) { - switch (X.getSubKind()) { - case nonloc::ConcreteIntKind: - return X.castAs<nonloc::ConcreteInt>().evalComplement(*this); - default: - return UnknownVal(); - } + // If resultType bitWith is higher that number of bits required + // to represent RHS, the sign flip produce same value. + return ValueBits < ResultType.getBitWidth(); } //===----------------------------------------------------------------------===// @@ -197,6 +198,17 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType()) ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); } + } else if (BinaryOperator::isAdditiveOp(op) && RHS.isNegative()) { + // Change a+(-N) into a-N, and a-(-N) into a+N + // Adjust addition/subtraction of negative value, to + // subtraction/addition of the negated value. + APSIntType resultIntTy = BasicVals.getAPSIntType(resultTy); + if (isNegationValuePreserving(RHS, resultIntTy)) { + ConvertedRHS = &BasicVals.getValue(-resultIntTy.convert(RHS)); + op = (op == BO_Add) ? BO_Sub : BO_Add; + } else { + ConvertedRHS = &BasicVals.Convert(resultTy, RHS); + } } else ConvertedRHS = &BasicVals.Convert(resultTy, RHS); @@ -286,7 +298,6 @@ static NonLoc doRearrangeUnchecked(ProgramStateRef State, else llvm_unreachable("Operation not suitable for unchecked rearrangement!"); - // FIXME: Can we use assume() without getting into an infinite recursion? if (LSym == RSym) return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt), nonloc::ConcreteInt(RInt), ResultTy) @@ -499,7 +510,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue(); // If we're dealing with two known constants, just perform the operation. - if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { + if (const llvm::APSInt *KnownRHSValue = getConstValue(state, rhs)) { llvm::APSInt RHSValue = *KnownRHSValue; if (BinaryOperator::isComparisonOp(op)) { // We're looking for a type big enough to compare the two values. @@ -619,7 +630,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } // For now, only handle expressions whose RHS is a constant. - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) { + if (const llvm::APSInt *RHSValue = getConstValue(state, rhs)) { // If both the LHS and the current expression are additive, // fold their constants and try again. if (BinaryOperator::isAdditiveOp(op)) { @@ -636,16 +647,26 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS()); const llvm::APSInt &second = IntType.convert(*RHSValue); + // If the op and lop agrees, then we just need to + // sum the constants. Otherwise, we change to operation + // type if substraction would produce negative value + // (and cause overflow for unsigned integers), + // as consequence x+1U-10 produces x-9U, instead + // of x+4294967287U, that would be produced without this + // additional check. const llvm::APSInt *newRHS; - if (lop == op) + if (lop == op) { newRHS = BasicVals.evalAPSInt(BO_Add, first, second); - else + } else if (first >= second) { newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + op = lop; + } else { + newRHS = BasicVals.evalAPSInt(BO_Sub, second, first); + } assert(newRHS && "Invalid operation despite common type!"); rhs = nonloc::ConcreteInt(*newRHS); lhs = nonloc::SymbolVal(symIntExpr->getLHS()); - op = lop; continue; } } @@ -656,7 +677,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, } // Is the RHS a constant? - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + if (const llvm::APSInt *RHSValue = getConstValue(state, rhs)) return MakeSymIntVal(Sym, op, *RHSValue, resultTy); if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy)) @@ -715,11 +736,41 @@ static SVal evalBinOpFieldRegionFieldRegion(const FieldRegion *LeftFR, llvm_unreachable("Fields not found in parent record's definition"); } +// This is used in debug builds only for now because some downstream users +// may hit this assert in their subsequent merges. +// There are still places in the analyzer where equal bitwidth Locs +// are compared, and need to be found and corrected. Recent previous fixes have +// addressed the known problems of making NULLs with specific bitwidths +// for Loc comparisons along with deprecation of APIs for the same purpose. +// +static void assertEqualBitWidths(ProgramStateRef State, Loc RhsLoc, + Loc LhsLoc) { + // Implements a "best effort" check for RhsLoc and LhsLoc bit widths + ASTContext &Ctx = State->getStateManager().getContext(); + uint64_t RhsBitwidth = + RhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(RhsLoc.getType(Ctx)); + uint64_t LhsBitwidth = + LhsLoc.getType(Ctx).isNull() ? 0 : Ctx.getTypeSize(LhsLoc.getType(Ctx)); + if (RhsBitwidth && LhsBitwidth && + (LhsLoc.getSubKind() == RhsLoc.getSubKind())) { + assert(RhsBitwidth == LhsBitwidth && + "RhsLoc and LhsLoc bitwidth must be same!"); + } +} + // FIXME: all this logic will change if/when we have MemRegion::getLocation(). SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, Loc rhs, QualType resultTy) { + + // Assert that bitwidth of lhs and rhs are the same. + // This can happen if two different address spaces are used, + // and the bitwidths of the address spaces are different. + // See LIT case clang/test/Analysis/cstring-checker-addressspace.c + // FIXME: See comment above in the function assertEqualBitWidths + assertEqualBitWidths(state, rhs, lhs); + // Only comparisons and subtractions are valid operations on two pointers. // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15]. // However, if a pointer is casted to an integer, evalBinOpNN may end up @@ -777,6 +828,8 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, return UnknownVal(); case loc::ConcreteIntKind: { + auto L = lhs.castAs<loc::ConcreteInt>(); + // If one of the operands is a symbol and the other is a constant, // build an expression for use by the constraint manager. if (SymbolRef rSym = rhs.getAsLocSymbol()) { @@ -785,19 +838,17 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, if (!BinaryOperator::isComparisonOp(op) || op == BO_Cmp) return UnknownVal(); - const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue(); op = BinaryOperator::reverseComparisonOp(op); - return makeNonLoc(rSym, op, lVal, resultTy); + return makeNonLoc(rSym, op, L.getValue(), resultTy); } // If both operands are constants, just perform the operation. if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { - SVal ResultVal = - lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt); - if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>()) - return evalCast(*Result, resultTy, QualType{}); + assert(BinaryOperator::isComparisonOp(op) || op == BO_Sub); - assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs"); + if (const auto *ResultInt = + BasicVals.evalAPSInt(op, L.getValue(), rInt->getValue())) + return evalCast(nonloc::ConcreteInt(*ResultInt), resultTy, QualType{}); return UnknownVal(); } @@ -805,7 +856,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // This must come after the test if the RHS is a symbol, which is used to // build constraints. The address of any non-symbolic region is guaranteed // to be non-NULL, as is any label. - assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>()); + assert((isa<loc::MemRegionVal, loc::GotoLabel>(rhs))); if (lhs.isZeroConstant()) { switch (op) { default: @@ -1114,9 +1165,8 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, return UnknownVal(); } -const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, - SVal V) { - V = simplifySVal(state, V); +const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state, + SVal V) { if (V.isUnknownOrUndef()) return nullptr; @@ -1132,6 +1182,11 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, return nullptr; } +const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, + SVal V) { + return getConstValue(state, simplifySVal(state, V)); +} + SVal SimpleSValBuilder::simplifyUntilFixpoint(ProgramStateRef State, SVal Val) { SVal SimplifiedVal = simplifySValOnce(State, Val); while (SimplifiedVal != Val) { @@ -1198,14 +1253,12 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { SVal VisitSymbolData(const SymbolData *S) { // No cache here. if (const llvm::APSInt *I = - SVB.getKnownValue(State, SVB.makeSymbolVal(S))) + State->getConstraintManager().getSymVal(State, S)) return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) : (SVal)SVB.makeIntVal(*I); return SVB.makeSymbolVal(S); } - // TODO: Support SymbolCast. - SVal VisitSymIntExpr(const SymIntExpr *S) { auto I = Cached.find(S); if (I != Cached.end()) @@ -1275,6 +1328,30 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); } + SVal VisitSymbolCast(const SymbolCast *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + const SymExpr *OpSym = S->getOperand(); + SVal OpVal = getConstOrVisit(OpSym); + if (isUnchanged(OpSym, OpVal)) + return skip(S); + + return cache(S, SVB.evalCast(OpVal, S->getType(), OpSym->getType())); + } + + SVal VisitUnarySymExpr(const UnarySymExpr *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + SVal Op = getConstOrVisit(S->getOperand()); + if (isUnchanged(S->getOperand(), Op)) + return skip(S); + + return cache( + S, SVB.evalUnaryOp(State, S->getOpcode(), Op, S->getType())); + } + SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); } SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); } @@ -1288,14 +1365,6 @@ SVal SimpleSValBuilder::simplifySValOnce(ProgramStateRef State, SVal V) { SVal VisitSVal(SVal V) { return V; } }; - // A crude way of preventing this function from calling itself from evalBinOp. - static bool isReentering = false; - if (isReentering) - return V; - - isReentering = true; SVal SimplifiedV = Simplifier(State).Visit(V); - isReentering = false; - return SimplifiedV; } |