[PATCH] D136603: [analyzer] Model cast after LValueToRValueBitCasts

Balázs Benics via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 24 07:39:03 PDT 2022


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 at -2 {{&SymRegion{reg_$0<void * p>}}}
+  // expected-warning at -2 {{&SymRegion{reg_$1<char (*)[8] array>}}}
+  clang_analyzer_dump((unsigned long)p);
+  clang_analyzer_dump(__builtin_bit_cast(unsigned long, p));
+  // expected-warning at -2 {{&SymRegion{reg_$0<void * p>} [as 64 bit integer]}}
+  // expected-warning at -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 at -2 {{&SymRegion{reg_$1<char (*)[8] array>} [as 64 bit integer]}}
+  // expected-warning at -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;


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D136603.470147.patch
Type: text/x-patch
Size: 3329 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20221024/5dbbed97/attachment.bin>


More information about the cfe-commits mailing list