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 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/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, + check::PreStmt> { + mutable std::unique_ptr 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(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 V = Val.getAs(); + if (V) + Value = " when value is " + V->getValue().toString(10); + + // Generate a report for this bug. + auto R = llvm::make_unique(*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(E->IgnoreParenCasts()); + if (!D) + return; + ProgramStateRef State = C.getState(); + State = State->remove(D->getDecl()); + C.addTransition(State); +} + +void ConversionChecker::setDeclVal(const Expr *E, SVal Val, CheckerContext &C) const { + const DeclRefExpr *D = dyn_cast(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()) + State = State->set(VD, Val); + else + State = State->remove(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(), DefinedVal2.castAs(), Bldr.getConditionType()); + if (GE.isUnknownOrUndef()) + return false; + ConstraintManager &CM = C.getStateManager().getConstraintManager(); + ProgramStateRef StateGE, StateLT; + std::tie(StateGE, StateLT) = CM.assumeDual(State, GE.castAs()); + return StateGE && !StateLT; +} + +void ConversionChecker::diagnoseLossOfPrecision(const BinaryOperator *B, CheckerContext &C) const { + const DeclRefExpr *D = dyn_cast(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(D->getDecl()); + if (!Val) + return; + + if (greaterEqual(C, *Val, MaxVal)) + reportBug(C, "Loss of precision", *Val); +} + +void ento::registerConversionChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} 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; +}