NoQ created this revision.
NoQ added reviewers: dcoughlin, xazax.hun, a_sidorin, george.karpenkov, 
rnkovacs, Szelethus, mikhail.ramalho, baloghadamsoftware.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, a.sidorin, szepet.

It is expected to have the same object (memory region) treated as if it has 
different types in different program points. Like, the object may be a union or 
contain a union, or a pointer to it can be reinterpreted as a pointer to a 
different type. The correct behavior for `RegionStore` when an object is stored 
as an object of type `T1` but loaded as an object of type `T2` is to store the 
object as if it has type `T1` but cast it to `T2` during load. This is what 
`CastRetrievedVal` is responsible for.

Note that the cast here is some sort of a "`reinterpret_cast`" (even in C). For 
instance, if you store a float and load an integer, you won't have your float 
rounded to an integer; instead, you will have garbage.

Therefore i propose to admit that we cannot perform the cast as long as types 
we're dealing with are non-trivial (neither integers, nor pointers). Our 
modeling would still be weird in this case when dealing with integer casts, but 
so is support for integer casts in general.

Of course, if the cast is not necessary (eg, `T1 == T2`), we can still load the 
value just fine. This trivial observation in fact improves our behavior on 
tests and addresses some old FIXMEs: previously we would have tried to perform 
the cast and fail. We should probably also teach `SValBuilder` to perform casts 
in this case.

Fixes https://bugs.llvm.org/show_bug.cgi?id=38668


Repository:
  rC Clang

https://reviews.llvm.org/D55875

Files:
  lib/StaticAnalyzer/Core/Store.cpp
  test/Analysis/bstring.cpp
  test/Analysis/casts.c
  test/Analysis/pointer-to-member.cpp

Index: test/Analysis/pointer-to-member.cpp
===================================================================
--- test/Analysis/pointer-to-member.cpp
+++ test/Analysis/pointer-to-member.cpp
@@ -253,11 +253,10 @@
   clang_analyzer_eval(&A::y); // expected-warning{{TRUE}}
   clang_analyzer_eval(&A::z); // expected-warning{{TRUE}}
 
-  // FIXME: These should be true.
   int A::*l = &A::x, A::*m = &A::y, A::*n = &A::z;
-  clang_analyzer_eval(l); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(m); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(n); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(l); // expected-warning{{TRUE}}
+  clang_analyzer_eval(m); // expected-warning{{TRUE}}
+  clang_analyzer_eval(n); // expected-warning{{TRUE}}
 
   // FIXME: These should be true as well.
   A a;
Index: test/Analysis/casts.c
===================================================================
--- test/Analysis/casts.c
+++ test/Analysis/casts.c
@@ -213,3 +213,14 @@
 }
 
 #endif
+
+char no_crash_SymbolCast_of_float_type_aux(int *p) {
+  *p += 1;
+  return *p;
+}
+
+void no_crash_SymbolCast_of_float_type() {
+  extern float x;
+  char (*f)() = no_crash_SymbolCast_of_float_type_aux;
+  f(&x);
+}
Index: test/Analysis/bstring.cpp
===================================================================
--- test/Analysis/bstring.cpp
+++ test/Analysis/bstring.cpp
@@ -44,7 +44,9 @@
 
   int i = buf[0];
 
+  // The TRUE warning shows up on the path on which the vector is empty.
   clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}}
+                                // expected-warning@-1 {{TRUE}}
 
   return buf;
 }
@@ -60,7 +62,9 @@
 
   int i = buf[0];
 
+  // The TRUE warning shows up on the path on which the vector is empty.
   clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}}
+                                // expected-warning@-1 {{TRUE}}
 
   return buf;
 }
Index: lib/StaticAnalyzer/Core/Store.cpp
===================================================================
--- lib/StaticAnalyzer/Core/Store.cpp
+++ lib/StaticAnalyzer/Core/Store.cpp
@@ -394,14 +394,28 @@
   return UnknownVal();
 }
 
+static bool isScalarEnoughToAttemptACast(QualType T) {
+  return T->isIntegralOrEnumerationType() || T->isAnyPointerType() ||
+         T->isReferenceType();
+}
+
 /// CastRetrievedVal - Used by subclasses of StoreManager to implement
 ///  implicit casts that arise from loads from regions that are reinterpreted
 ///  as another region.
 SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
-                                    QualType castTy) {
-  if (castTy.isNull() || V.isUnknownOrUndef())
+                                    QualType CastTy) {
+  if (CastTy.isNull() || V.isUnknownOrUndef())
     return V;
 
+  QualType OrigTy = R->getValueType();
+
+  if (!isScalarEnoughToAttemptACast(OrigTy) ||
+      !isScalarEnoughToAttemptACast(CastTy)) {
+    if (OrigTy == CastTy)
+      return V;
+    return UnknownVal();
+  }
+
   // When retrieving symbolic pointer and expecting a non-void pointer,
   // wrap them into element regions of the expected type if necessary.
   // SValBuilder::dispatchCast() doesn't do that, but it is necessary to
@@ -410,13 +424,13 @@
   // We might need to do that for non-void pointers as well.
   // FIXME: We really need a single good function to perform casts for us
   // correctly every time we need it.
-  if (castTy->isPointerType() && !castTy->isVoidPointerType())
+  if (CastTy->isPointerType() && !CastTy->isVoidPointerType())
     if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion()))
       if (SR->getSymbol()->getType().getCanonicalType() !=
-          castTy.getCanonicalType())
-        return loc::MemRegionVal(castRegion(SR, castTy));
+          CastTy.getCanonicalType())
+        return loc::MemRegionVal(castRegion(SR, CastTy));
 
-  return svalBuilder.dispatchCast(V, castTy);
+  return svalBuilder.dispatchCast(V, CastTy);
 }
 
 SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to