danielmarjamaki updated this revision to Diff 40505. danielmarjamaki added a comment.
I have problems with the "default" handling of expressions. I want to warn about loss of precision for such code: unsigned int x = 256; unsigned char c; c = x; The analyser tells me that the RHS value is 0 instead of 256 in the assignment "c = x". Here is the dump of the ProgramState: Store (direct and default bindings), 0x0 : Expressions: (0x7b9bd30,0x7b458d8) c : &c (0x7b9bd30,0x7b45940) x : 0 U8b Ranges are empty. This patch is a quick proof-of-concept where I track variable values myself using ProgramState. It gives me better results when I scan projects. I wonder if you think I should continue working on this proof-of-concept method. Or if you have some tip how I can see the untruncated rhs value in a assignment. http://reviews.llvm.org/D13126 Files: lib/StaticAnalyzer/Checkers/CMakeLists.txt lib/StaticAnalyzer/Checkers/Checkers.td lib/StaticAnalyzer/Checkers/ConversionChecker.cpp test/Analysis/conversion.c
Index: test/Analysis/conversion.c =================================================================== --- test/Analysis/conversion.c +++ test/Analysis/conversion.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.Conversion -verify %s + +void warn1() { + unsigned short s; + unsigned int largeValue = 1 << 16; + s = largeValue; // expected-warning{{Loss of precision when value is 65536}} +} + +void warn2(int x) { + unsigned short s; + unsigned int largeValue = 1; + if (x) + largeValue = 1 << 16; + s = largeValue; // expected-warning{{Loss of precision when value is 65536}} +} + +void dontwarn1() { + unsigned short s; + unsigned int largeValue = 1 << 16; + s = (unsigned short)largeValue; +} Index: lib/StaticAnalyzer/Checkers/ConversionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -0,0 +1,170 @@ +//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ConversionChecker that warns about dangerous conversions where +// there is possible loss of sign or loss of precision. +// +//===----------------------------------------------------------------------===// +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class ConversionChecker : public Checker<check::PreStmt<BinaryOperator>, + check::PreStmt<UnaryOperator>, + check::PreStmt<DeclStmt>> { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const { + if (B->isAssignmentOp()) { + //diagnoseLossOfSign(B, C); + diagnoseLossOfPrecision(B, C); + assign(B, C); + } + else if (B->isRelationalOp() || B->isMultiplicativeOp()) { + //diagnoseLossOfSign(B, C); + } + } + + void checkPreStmt(const UnaryOperator *U, CheckerContext &C) const { + UnaryOperator::Opcode Opc = U->getOpcode(); + if (Opc == UO_AddrOf || Opc == UO_PreInc || Opc == UO_PreDec || Opc == UO_PostInc || Opc == UO_PostDec) + removeDecl(U->getSubExpr(), C); + } + + void checkPreStmt(const DeclStmt *D, CheckerContext &C) const { + const VarDecl *VD = dyn_cast<VarDecl>(D->getSingleDecl()); + if (!VD) + return; + const Expr *Init = VD->getInit(); + if (!Init) + return; + SVal Val = C.getSVal(Init); + if (Val.isUnknown()) + return; + setDeclVal(VD, Val, C); + } + + +private: + void assign(const BinaryOperator *B, CheckerContext &C) const; + void removeDecl(const Expr *E, CheckerContext &C) const; + void setDeclVal(const Expr *E, SVal Val, CheckerContext &C) const; + void setDeclVal(const ValueDecl *VD, SVal Val, CheckerContext &C) const; + + void diagnoseLossOfPrecision(const BinaryOperator *B, CheckerContext &C) const; + + void reportBug(CheckerContext &C, const char Msg[], SVal Val) const { + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(C.getState()); + if (!N) + return; + + if (!BT) + BT.reset(new BuiltinBug(this, "Conversion", "Loss of sign/precision.")); + + std::string Value; + Optional<nonloc::ConcreteInt> V = Val.getAs<nonloc::ConcreteInt>(); + if (V) + Value = " when value is " + V->getValue().toString(10); + + // Generate a report for this bug. + auto R = llvm::make_unique<BugReport>(*BT, Msg + Value, N); + C.emitReport(std::move(R)); + } +}; +} + +REGISTER_MAP_WITH_PROGRAMSTATE(DeclVal, const ValueDecl *, SVal) + +void ConversionChecker::assign(const BinaryOperator *B, CheckerContext &C) const { + SVal Val = C.getSVal(B->getRHS()); + if (Val.isUnknown() || B->getOpcode() != BO_Assign) + removeDecl(B->getLHS(), C); + else + setDeclVal(B->getLHS(), Val, C); +} + +void ConversionChecker::removeDecl(const Expr *E, CheckerContext &C) const { + const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (!D) + return; + ProgramStateRef State = C.getState(); + State = State->remove<DeclVal>(D->getDecl()); + C.addTransition(State); +} + +void ConversionChecker::setDeclVal(const Expr *E, SVal Val, CheckerContext &C) const { + const DeclRefExpr *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (D) + setDeclVal(D->getDecl(), Val, C); +} + +void ConversionChecker::setDeclVal(const ValueDecl *VD, SVal Val, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (Val.getAs<nonloc::ConcreteInt>()) + State = State->set<DeclVal>(VD, Val); + else + State = State->remove<DeclVal>(VD); + C.addTransition(State); +} + +static bool greaterEqual(CheckerContext &C, const SVal &Val1, unsigned long long Val2) { + ProgramStateRef State = C.getState(); + SValBuilder &Bldr = C.getSValBuilder(); + DefinedSVal DefinedVal2 = Bldr.makeIntVal(Val2, C.getASTContext().LongLongTy); + + // Is Val1 greater or equal than Val2? + SVal GE = Bldr.evalBinOpNN(State, BO_GE, Val1.castAs<NonLoc>(), DefinedVal2.castAs<NonLoc>(), Bldr.getConditionType()); + if (GE.isUnknownOrUndef()) + return false; + ConstraintManager &CM = C.getStateManager().getConstraintManager(); + ProgramStateRef StateGE, StateLT; + std::tie(StateGE, StateLT) = CM.assumeDual(State, GE.castAs<DefinedSVal>()); + return StateGE && !StateLT; +} + +void ConversionChecker::diagnoseLossOfPrecision(const BinaryOperator *B, CheckerContext &C) const { + const DeclRefExpr *D = dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenImpCasts()); + if (!D) + return; + + QualType LT = B->getLHS()->IgnoreParenCasts()->getType(); + QualType RT = B->getRHS()->IgnoreParenCasts()->getType(); + + if (!LT.getTypePtr()->isIntegerType() || !RT.getTypePtr()->isIntegerType()) + return; + + if (C.getASTContext().getIntWidth(LT) >= C.getASTContext().getIntWidth(RT)) + return; + + unsigned LW = C.getASTContext().getIntWidth(LT); + if (LW >= 64U) + return; + + unsigned long long MaxVal = 1ULL << LW; + + ProgramStateRef State = C.getState(); + const SVal *Val = State->get<DeclVal>(D->getDecl()); + if (!Val) + return; + + if (greaterEqual(C, *Val, MaxVal)) + reportBug(C, "Loss of precision", *Val); +} + +void ento::registerConversionChecker(CheckerManager &mgr) { + mgr.registerChecker<ConversionChecker>(); +} Index: lib/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- lib/StaticAnalyzer/Checkers/Checkers.td +++ lib/StaticAnalyzer/Checkers/Checkers.td @@ -127,6 +127,10 @@ HelpText<"Check for cast from non-struct pointer to struct pointer">, DescFile<"CastToStructChecker.cpp">; +def ConversionChecker : Checker<"Conversion">, + HelpText<"Loss of sign/precision in conversions">, + DescFile<"ConversionChecker.cpp">; + def IdenticalExprChecker : Checker<"IdenticalExpr">, HelpText<"Warn about unintended use of identical expressions in operators">, DescFile<"IdenticalExprChecker.cpp">; Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -26,6 +26,7 @@ CheckSizeofPointer.cpp CheckerDocumentation.cpp ChrootChecker.cpp + ConversionChecker.cpp ClangCheckers.cpp DeadStoresChecker.cpp DebugCheckers.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits