steakhal created this revision.
steakhal added reviewers: ASDenysPetrov, martong, xazax.hun, NoQ, Szelethus.
Herald added subscribers: manas, dkrupp, donat.nagy, mikhail.ramalho, 
a.sidorin, rnkovacs, szepet, baloghadamsoftware.
Herald added a project: All.
steakhal requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Casting a pointer to a suitably large integral type by reinterpret-cast
should result in the same value as by using the `__builtin_bit_cast()`.
The compiler exploits this: https://godbolt.org/z/zMP3sG683

However, the analyzer does not bind the same symbolic value to these
expressions, resulting in weird situations, such as failing equality
checks and even results in crashes: https://godbolt.org/z/oeMP7cj8q

Previously, the `&SymRegion` were not cast to `NonLoc`, which later in
the `evalBinOpLN()` caused a crash when the engine wanted to calculate
the offset at the BinaryOperator expression.
So, in this patch, I'm proposing to add an explicit cast when evaluating
an `LValueToRValueBitCast` cast expression. Other than casting the
resulting value to the given type before binding it to the expression it
does the same thing that we do for `LValueToRValue` casts.

Here is a snippet of code, annotated by the previous and new dump values.
`LocAsInteger` wraps the `SymRegion` in only two cases:

  void LValueToRValueBitCast_dumps(void *p, char (*array)[8]) {
    clang_analyzer_dump(p);     // remained: &SymRegion{reg_$0<void * p>}
    clang_analyzer_dump(array); // remained: {{&SymRegion{reg_$1<char (*)[8] 
array>}
    clang_analyzer_dump((unsigned long)p);
    // remained: {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
    clang_analyzer_dump(__builtin_bit_cast(unsigned long, p));     <--------- 
change #1
    // previously: {{&SymRegion{reg_$0<void * p>}}}
    // now:        {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
    clang_analyzer_dump((unsigned long)array); // remained: 
{{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}}
    clang_analyzer_dump(__builtin_bit_cast(unsigned long, array)); <--------- 
change #2
    // previously: {{&SymRegion{reg_$1<char (*)[8] array>}}}
    // now:        {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}}
  }

PS: I'm not sure how/when we should get rid of the `LocAsInteger` and
represent this by a `SymbolCast`.
Maybe @ASDenysPetrov or @martong could help me review this.

This is a follow-up of D105017 <https://reviews.llvm.org/D105017>, where I 
thought it should be enough to
model `LValueToRValueBitCast` the same way as we do with `LValueToRValueCast`.
I still think the same, but we really should have some `NonLoc` value here, IMO.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D136603

Files:
  clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
  clang/test/Analysis/ptr-arith.cpp


Index: clang/test/Analysis/ptr-arith.cpp
===================================================================
--- clang/test/Analysis/ptr-arith.cpp
+++ clang/test/Analysis/ptr-arith.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 
-analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm -verify %s
+// RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 -verify %s -triple 
x86_64-pc-linux-gnu \
+// RUN:    -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm
 
 template <typename T> void clang_analyzer_dump(T);
 
@@ -141,3 +142,22 @@
   return bits->b; // no-warning
 }
 } // namespace Bug_55934
+
+void LValueToRValueBitCast_dumps(void *p, char (*array)[8]) {
+  clang_analyzer_dump(p);
+  clang_analyzer_dump(array);
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>}}}
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>}}}
+  clang_analyzer_dump((unsigned long)p);
+  clang_analyzer_dump(__builtin_bit_cast(unsigned long, p));
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
+  clang_analyzer_dump((unsigned long)array);
+  clang_analyzer_dump(__builtin_bit_cast(unsigned long, array));
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit 
integer]}}
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit 
integer]}}
+}
+
+unsigned long ptr_arithmetic(void *p) {
+  return __builtin_bit_cast(unsigned long, p) + 1; // no-crash
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -288,8 +288,36 @@
   ExplodedNodeSet dstPreStmt;
   getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
 
-  if (CastE->getCastKind() == CK_LValueToRValue ||
-      CastE->getCastKind() == CK_LValueToRValueBitCast) {
+  if (CastE->getCastKind() == CK_LValueToRValueBitCast) {
+    // Do the same as for 'CK_LValueToRValue' but cast the resulting value to
+    // the appropriate type before binding.
+    for (ExplodedNode *subExprNode : dstPreStmt) {
+      ProgramStateRef state = subExprNode->getState();
+      const LocationContext *LCtx = subExprNode->getLocationContext();
+      Optional<Loc> Location = state->getSVal(Ex, LCtx).getAs<Loc>();
+      if (!Location)
+        return;
+
+      ExplodedNodeSet Tmp;
+      evalLocation(Tmp, CastE, CastE, subExprNode, state, *Location, true);
+      if (Tmp.empty())
+        return;
+
+      // Proceed with the load.
+      StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
+      for (ExplodedNode *I : Tmp) {
+        state = I->getState();
+
+        SVal V = state->getSVal(*Location, CastE->getType());
+        SVal CastV = getSValBuilder().evalCast(V, CastE->getType(), 
QualType{});
+        Bldr.generateNode(CastE, I, state->BindExpr(CastE, LCtx, CastV),
+                          nullptr, ProgramPoint::PostLoadKind);
+      }
+    }
+    return;
+  }
+
+  if (CastE->getCastKind() == CK_LValueToRValue) {
     for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = 
dstPreStmt.end();
          I!=E; ++I) {
       ExplodedNode *subExprNode = *I;


Index: clang/test/Analysis/ptr-arith.cpp
===================================================================
--- clang/test/Analysis/ptr-arith.cpp
+++ clang/test/Analysis/ptr-arith.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm -verify %s
+// RUN: %clang_analyze_cc1 -Wno-unused-value -std=c++14 -verify %s -triple x86_64-pc-linux-gnu \
+// RUN:    -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm
 
 template <typename T> void clang_analyzer_dump(T);
 
@@ -141,3 +142,22 @@
   return bits->b; // no-warning
 }
 } // namespace Bug_55934
+
+void LValueToRValueBitCast_dumps(void *p, char (*array)[8]) {
+  clang_analyzer_dump(p);
+  clang_analyzer_dump(array);
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>}}}
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>}}}
+  clang_analyzer_dump((unsigned long)p);
+  clang_analyzer_dump(__builtin_bit_cast(unsigned long, p));
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
+  // expected-warning@-2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
+  clang_analyzer_dump((unsigned long)array);
+  clang_analyzer_dump(__builtin_bit_cast(unsigned long, array));
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}}
+  // expected-warning@-2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}}
+}
+
+unsigned long ptr_arithmetic(void *p) {
+  return __builtin_bit_cast(unsigned long, p) + 1; // no-crash
+}
Index: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -288,8 +288,36 @@
   ExplodedNodeSet dstPreStmt;
   getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
 
-  if (CastE->getCastKind() == CK_LValueToRValue ||
-      CastE->getCastKind() == CK_LValueToRValueBitCast) {
+  if (CastE->getCastKind() == CK_LValueToRValueBitCast) {
+    // Do the same as for 'CK_LValueToRValue' but cast the resulting value to
+    // the appropriate type before binding.
+    for (ExplodedNode *subExprNode : dstPreStmt) {
+      ProgramStateRef state = subExprNode->getState();
+      const LocationContext *LCtx = subExprNode->getLocationContext();
+      Optional<Loc> Location = state->getSVal(Ex, LCtx).getAs<Loc>();
+      if (!Location)
+        return;
+
+      ExplodedNodeSet Tmp;
+      evalLocation(Tmp, CastE, CastE, subExprNode, state, *Location, true);
+      if (Tmp.empty())
+        return;
+
+      // Proceed with the load.
+      StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
+      for (ExplodedNode *I : Tmp) {
+        state = I->getState();
+
+        SVal V = state->getSVal(*Location, CastE->getType());
+        SVal CastV = getSValBuilder().evalCast(V, CastE->getType(), QualType{});
+        Bldr.generateNode(CastE, I, state->BindExpr(CastE, LCtx, CastV),
+                          nullptr, ProgramPoint::PostLoadKind);
+      }
+    }
+    return;
+  }
+
+  if (CastE->getCastKind() == CK_LValueToRValue) {
     for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
          I!=E; ++I) {
       ExplodedNode *subExprNode = *I;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to