Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -3485,6 +3485,11 @@ static bool isBitwiseOp(Opcode Opc) { return Opc >= BO_And && Opc <= BO_Or; } bool isBitwiseOp() const { return isBitwiseOp(getOpcode()); } + static bool isBitwiseOrShiftOp(Opcode Opc) { + return isBitwiseOp(Opc) || isShiftOp(Opc); + } + bool isBitwiseOrShiftOp() const { return isBitwiseOrShiftOp(getOpcode()); } + static bool isRelationalOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_GE; } bool isRelationalOp() const { return isRelationalOp(getOpcode()); } Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h @@ -118,6 +118,7 @@ RangeSet Negate(BasicValueFactory &BV, Factory &F) const; void print(raw_ostream &os) const; + LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); } bool operator==(const RangeSet &other) const { return ranges == other.ranges; Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -476,6 +476,53 @@ return Input; } +// We do not support evaluating bitwise operations, so that when we check for +// their results being null we create a new assumption whether the current new +// symbol is null or non-null. If we are on the non-null assumption's branch +// we need to check the left-hand side operand's constraint range informations: +// - It if contradicts with the forming new range 'RS' then it returns a null +// state as it is an impossible condition. +// - Otherwise it removes the nullability from its ranges as we know that it +// cannot be null on that branch (except bitwise OR). +static ProgramStateRef applyBitwiseRanges(ProgramStateRef State, + BasicValueFactory &BV, + RangeSet::Factory &F, RangeSet RS, + SymbolRef Sym) { + if (RS.isEmpty()) + return State; + + // If we cannot be sure whether the value is non-null, do nothing. + const llvm::APSInt &Zero = BV.getAPSIntType(Sym->getType()).getZeroValue(); + if (!RS.Intersect(BV, F, Zero, Zero).isEmpty()) + return State; + + const SymIntExpr *SIE = dyn_cast(Sym); + if (!SIE) + return State; + + // Bitwise OR could be null. + BinaryOperator::Opcode Op = SIE->getOpcode(); + if (!BinaryOperator::isBitwiseOrShiftOp(Op) || Op == BO_Or) + return State; + + ConstraintRangeTy Constraints = State->get(); + const SymExpr *CurrentSym = SIE->getLHS(); + if (const RangeSet *CurrentRS = Constraints.lookup(CurrentSym)) { + const RangeSet NewRS = assumeNonZero(BV, F, CurrentSym, *CurrentRS); + + // If the 'NewRS' is not empty it means the 'CurrentSym' is not assumed + // to be concrete zero, so it does not contradicts with the non-null + // assumption and we could apply the new constraint ranges. + if (!NewRS.isEmpty()) { + State = State->set(CurrentSym, NewRS); + } else { + return nullptr; + } + } + + return State; +} + RangeSet RangeConstraintManager::getRange(ProgramStateRef State, SymbolRef Sym) { ConstraintRangeTy::data_type *V = State->get(Sym); @@ -567,6 +614,10 @@ // [Int-Adjustment+1, Int-Adjustment-1] // Notice that the lower bound is greater than the upper bound. RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); + + if (!(St = applyBitwiseRanges(St, getBasicVals(), F, New, Sym))) + return nullptr; + return New.isEmpty() ? nullptr : St->set(Sym, New); } @@ -582,6 +633,10 @@ // [Int-Adjustment, Int-Adjustment] llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); + + if (!(St = applyBitwiseRanges(St, getBasicVals(), F, New, Sym))) + return nullptr; + return New.isEmpty() ? nullptr : St->set(Sym, New); } Index: clang/test/Analysis/bitwise-nullability.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/bitwise-nullability.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-output=text -verify %s + +typedef unsigned long long uint64_t; + +void test_null_lhs_range_to_non_null_result_infeasible(uint64_t Magic) { + uint64_t MaskedMagic = Magic & (0xffffffffffffffffULL >> 13); + + if (Magic) { + // no-note: 'Assuming 'Magic' is 0' was here. + return; + } + + if (MaskedMagic) { + // no-note: 'Assuming 'MaskedMagic' is not equal to 0' was here. + (void)(1 / Magic); // no-warning + } +} + +void test_non_null_lhs_range_to_null_result_feasible(uint64_t Magic) { + uint64_t MaskedMagic = Magic & (0xffffffffffffffffULL >> 13); + + if (!Magic) { + // expected-note@-1 {{Assuming 'Magic' is not equal to 0}} + // expected-note@-2 {{Taking false branch}} + return; + } + + if (!MaskedMagic) { + // expected-note@-1 {{Assuming 'MaskedMagic' is 0}} + // expected-note@-2 {{Taking true branch}} + (void)(1 / !Magic); + // expected-note@-1 {{'Magic' is not equal to 0}} + // expected-note@-2 {{Division by zero}} + // expected-warning@-3 {{Division by zero}} + } +} Index: clang/test/Analysis/bitwise-ranges.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/bitwise-ranges.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -verify %s + +void clang_analyzer_eval(int); + +void test_range(unsigned int X) { + if (X < 2 || X > 3) + return; + + clang_analyzer_eval(X >= 2 && X <= 3); + // expected-warning@-1 {{TRUE}} + + // We have no non-null constraint range information available for 'A'. + unsigned int A = X | 8; + clang_analyzer_eval(A == 0); + // expected-warning@-1 {{FALSE}} + + unsigned int B = X & 8; + clang_analyzer_eval((B >= 1 && B <= 8) || B == 0); + // expected-warning@-1 {{TRUE}} + + unsigned int C = X & 1; + clang_analyzer_eval(C == 1 || C == 0); + // expected-warning@-1 {{TRUE}} + + void(X / 0); + // expected-warning@-1 {{division by zero is undefined}} + // expected-warning@-2 {{Division by zero}} +}