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

Reply via email to