ASDenysPetrov updated this revision to Diff 394290.
ASDenysPetrov added a comment.

Changed handler `check::` functions. Reworked. Covered more cases.

Several cases left (marked as FIXME in the test file). For the glance some of 
them we can't handle because of (possibly) wrong symbolic modeling.

WIP.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D114718/new/

https://reviews.llvm.org/D114718

Files:
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
  clang/lib/StaticAnalyzer/Checkers/StrictAliasingChecker.cpp
  clang/test/Analysis/Checkers/StrictAliasingChecker/strict-aliasing.cpp

Index: clang/test/Analysis/Checkers/StrictAliasingChecker/strict-aliasing.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/Checkers/StrictAliasingChecker/strict-aliasing.cpp
@@ -0,0 +1,811 @@
+// RUN: %clang_cc1 -std=c++20 -analyze -analyzer-config eagerly-assume=false -analyzer-checker=debug.ExprInspection,alpha.core.StrictAliasing -verify %s
+// NOTE: -relaxed-aliasing flag disables StrictAliasing checker.
+
+template <typename T>
+void clang_analyzer_dump(T x);
+void clang_analyzer_eval(int);
+
+namespace std {
+enum class byte : unsigned char {};
+enum class OtherByte : unsigned char {};
+}; // namespace std
+enum class EnumInt : int {};
+
+union UnionUchar {
+  unsigned char x;
+  char y;
+};
+union UnionInt {
+  int x;
+};
+class Class {};
+class ClassInt {
+public:
+  int x;
+};
+class Base {
+public:
+  int x;
+};
+struct AnotherBase {
+  int y;
+};
+class Derived : public AnotherBase, public Base {
+  int z;
+};
+class MostDerived : public Derived {
+  int w;
+};
+
+using AliasedStdByte = std::byte;
+using AliasedChar = char;
+using AliasedSChar = const signed char;
+using AliasedInt = int;
+using AliasedUInt = const unsigned int;
+using AliasedULong = unsigned long;
+using AliasedBase = Base;
+using AliasedAnotherBase = AnotherBase;
+
+namespace ns1 {
+
+void int_casts() {
+  using MyInt = int;
+  MyInt x = {};
+  // int to records
+  {
+    auto *ptr = (Class *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (ClassInt *)&x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionUchar *)&x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionInt *)&x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  // int to records
+  {
+    auto *ptr = (std::byte *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (std::OtherByte *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior. Attempting to access the stored value of type}}
+  }
+  {
+    auto *ptr = (EnumInt *)&x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  // int to scalars
+  {
+    auto *ptr = (const char *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (unsigned char *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed char *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (short *)&x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned short *)&x;
+    ptr[0] = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed short *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (int *)&x;
+    ptr[0] = 42; // no-warning
+  }
+  {
+    auto *ptr = (const volatile unsigned int *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed int *)&x;
+    *ptr = 24; // no-warning
+  }
+  {
+    auto *ptr = (long *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long long *)&x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long long *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long long *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (float *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (double *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long double *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  // int to aliases
+  {
+    auto *ptr = (AliasedStdByte *)&x;
+    auto y = ptr[0]; // no-warning
+  }
+  {
+    auto *ptr = (AliasedChar *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedSChar *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)&x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedInt *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedUInt *)&x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedULong *)&x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+}
+
+void intptr_casts(int *x) {
+  // int to records
+  {
+    auto *ptr = (Class *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (ClassInt *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionUchar *)x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionInt *)x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  // int to records
+  {
+    auto *ptr = (std::byte *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (std::OtherByte *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior. Attempting to access the stored value of type}}
+  }
+  {
+    auto *ptr = (EnumInt *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  // int to scalars
+  {
+    auto *ptr = (const char *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (unsigned char *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed char *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (short *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned short *)x;
+    ptr[0] = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed short *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (int *)x;
+    ptr[0] = 42; // no-warning
+  }
+  {
+    auto *ptr = (const volatile unsigned int *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed int *)x;
+    *ptr = 24; // no-warning
+  }
+  {
+    auto *ptr = (long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long long *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (float *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (double *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long double *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  // int to aliases
+  {
+    auto *ptr = (AliasedStdByte *)x;
+    auto y = ptr[0]; // no-warning
+  }
+  {
+    auto *ptr = (AliasedChar *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedSChar *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedInt *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedUInt *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedULong *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+}
+
+void intptr_casts2() {
+  int *x = 0;
+  // int to records
+  {
+    auto *ptr = (Class *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (ClassInt *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionUchar *)x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionInt *)x;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  // int to records
+  {
+    auto *ptr = (std::byte *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (std::OtherByte *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior. Attempting to access the stored value of type}}
+  }
+  {
+    auto *ptr = (EnumInt *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  // int to scalars
+  {
+    auto *ptr = (const char *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (unsigned char *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed char *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (short *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned short *)x;
+    ptr[0] = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed short *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (int *)x;
+    // FIXME: This shall not warn.
+    ptr[0] = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (const volatile unsigned int *)x;
+    // FIXME: This shall not warn.
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed int *)x;
+    // FIXME: This shall not warn.
+    *ptr = 24; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long long *)x;
+    // FIXME: This should warn {{Undefined behavior}}.
+    auto y = ptr[0]; // no-warning
+  }
+  {
+    auto *ptr = (unsigned long long *)x;
+    // FIXME: This should warn {{Undefined behavior}}.
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed long long *)x;
+    // FIXME: This should warn {{Undefined behavior}}.
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (float *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (double *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long double *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  // int to aliases
+  {
+    auto *ptr = (AliasedStdByte *)x;
+    auto y = ptr[0]; // no-warning
+  }
+  {
+    auto *ptr = (AliasedChar *)x;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedSChar *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)x;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedInt *)x;
+    // FIXME: This shall not warn.
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedUInt *)x;
+    // FIXME: This shall not warn.
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)x;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+}
+
+void derived_class_casts() {
+  Derived d;
+  // record to records
+  {
+    auto *ptr = (MostDerived *)&d;
+    // FIXME: This should warn {{Undefined behavior}}.
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (Derived *)&d;
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (Base *)&d;
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (const AnotherBase *)&d;
+    auto y = ptr->y; // no-warning
+  }
+  {
+    auto *ptr = (ClassInt *)&d;
+    auto y = ptr->x; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (Class *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (const ClassInt *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionUchar *)&d;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionInt *)&d;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  // record to scalars
+  {
+    auto *ptr = (char *)&d;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (const unsigned char *)&d;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed char *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (short *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned short *)&d;
+    ptr[0] = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed short *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (int *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned int *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed int *)&d;
+    *ptr = 24; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long *)&d;
+    auto y = ptr[0]; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long long *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long long *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long long *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (float *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (double *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long double *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  // record to aliases
+  {
+    auto *ptr = (AliasedStdByte *)&d;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedChar *)&d;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedSChar *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedInt *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedUInt *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)&d;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedBase *)&d;
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (AliasedAnotherBase *)&d;
+    auto y = (*ptr).y; // no-warning
+  }
+}
+
+void base_class_casts() {
+  Base b;
+  // record to records
+  {
+    auto *ptr = (MostDerived *)&b;
+    // FIXME: This should warn {{Undefined behavior}}.
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (Derived *)&b;
+    // FIXME: This should warn {{Undefined behavior}}.
+    ptr->x = 42; // no-warning
+  }
+  {
+    auto *ptr = (Base *)&b;
+    auto y = ptr->x; // no-warning
+  }
+  {
+    auto *ptr = (const AnotherBase *)&b;
+    auto y = (*ptr).y; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (ClassInt *)&b;
+    auto y = ptr[0].x; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (Class *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (const ClassInt *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionUchar *)&b;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (UnionInt *)&b;
+    ptr->x = 42; // expected-warning{{Undefined behavior}}
+  }
+  // record to scalars
+  {
+    auto *ptr = (char *)&b;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (const unsigned char *)&b;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (signed char *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (short *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned short *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed short *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (int *)&b;
+    ptr[0] = 24; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned int *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed int *)&b;
+    *ptr = 24; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (unsigned long long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (signed long long *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (float *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (double *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (long double *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  // record to aliases
+  {
+    auto *ptr = (AliasedStdByte *)&b;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedChar *)&b;
+    auto y = *ptr; // no-warning
+  }
+  {
+    auto *ptr = (AliasedSChar *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedInt *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedUInt *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedULong *)&b;
+    auto y = *ptr; // expected-warning{{Undefined behavior}}
+  }
+  {
+    auto *ptr = (AliasedBase *)&b;
+    auto y = ptr[0].x; // no-warning
+  }
+  {
+    auto *ptr = (AliasedAnotherBase *)&b;
+    auto y = (*ptr).y; // expected-warning{{Undefined behavior}}
+  }
+}
+
+// This produces nested CXXBaseObjectRegion.
+void nested_casts() {
+  MostDerived md;
+  {
+    auto *ptr1 = (Derived *)&md; // Base{md,Derived}
+    auto *ptr2 = (Base *)ptr1;   // Base{Base{md,Derived},Base}
+    auto y = ptr2->x;            // no-warning
+  }
+  {
+    auto *ptr1 = (Base *)&md;
+    auto *ptr2 = (Derived *)ptr1;
+    auto y = ptr2->x; // no-warning
+  }
+  {
+    auto *ptr1 = (Base *)&md;
+    auto *ptr2 = (AnotherBase *)ptr1;
+    auto *ptr3 = (MostDerived *)ptr2;
+    ptr3->x = 42; // no-warning
+  }
+  {
+    auto *ptr1 = (Base *)&md;
+    auto *ptr2 = (MostDerived *)ptr1;
+    auto *ptr3 = (char *)ptr2;
+    auto y = *ptr3; // no-warning
+  }
+  {
+    auto *ptr1 = (UnionInt *)&md;
+    auto *ptr2 = (char *)ptr1;
+    auto *ptr3 = (Base *)ptr2;
+    auto y = ptr3->x; // no-warning
+  }
+  {
+    auto *ptr1 = (AliasedUInt *)&md;
+    auto *ptr2 = (short *)ptr1;
+    auto *ptr3 = (AliasedStdByte *)ptr2;
+    auto y = ptr3[0]; // no-warning
+  }
+}
+
+} // namespace ns1
Index: clang/lib/StaticAnalyzer/Checkers/StrictAliasingChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/StrictAliasingChecker.cpp
@@ -0,0 +1,201 @@
+//===- StrictAliasingChecker - ... ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// StrictAliasingChecker implements checks on violation of the next paragraph of
+// the Standard which is known as `Strict Aliasing Rule`.
+// C++20 7.2.1 p11 [basic.lval]:
+//  If a program attempts to access the stored value of an object through a
+//  glvalue whose type is not similar to one of the following types the behavior
+//  is undefined:
+//  - the dynamic type of the object,
+//  - a type that is the signed or unsigned type corresponding to the dynamic
+//    type of the object, or
+//  - a char, unsigned char, or std::byte type.
+//
+// NOTE: The checker operates only when strict-aliasing is enabled with
+// corresponding compiler flag.
+//
+// NOTE: There are differences in strict aliasing rules between C, C++
+// Standards. Now we only impelement checks since C++20 Standard (there were
+// changes applied in C++20 http://wg21.link/cwg2051).
+//
+// TODO: Support C++98/11/14/17. Shall we?
+// TODO: Support C.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class AccessInferrer {
+  QualType From;
+  QualType To;
+  ASTContext &Ctx;
+
+public:
+  // Check whether the given types submit to the Strict Aliasing Rule.
+  //
+  // NOTE: User must provide canonical and unqualified QualType's for the
+  // correct result.
+  static bool canAccess(QualType From, QualType To, ASTContext &Ctx) {
+    // NOTE: Despite the function name `isCanonical()`, it also check whether
+    // the type is unqualified. (See implementation)
+    assert(From.isCanonical() && "The type shall be an unqualified canonical.");
+    assert(To.isCanonical() && "The type shall be an unqualified canonical.");
+    AccessInferrer AI(From, To, Ctx);
+    return AI.canAccessImpl();
+  }
+
+private:
+  AccessInferrer(QualType From, QualType To, ASTContext &Ctx)
+      : From(From), To(To), Ctx(Ctx) {}
+  bool canAccessImpl() {
+    return isSameDynamic() || isCharOrByte() || isOppositeSign();
+  }
+  // - the dynamic type of the object
+  bool isSameDynamic() {
+    if (From == To)
+      return true;
+
+    if (const CXXRecordDecl *FromDecl = From->getAsCXXRecordDecl())
+      if (const CXXRecordDecl *ToDecl = To->getAsCXXRecordDecl())
+        return FromDecl->isDerivedFrom(ToDecl);
+
+    return false;
+  }
+  // - a char, unsigned char, or std::byte type.
+  bool isCharOrByte() {
+    return To == Ctx.CharTy || To == Ctx.UnsignedCharTy || To->isStdByteType();
+  }
+  // - a type that is the signed or unsigned type corresponding to the dynamic
+  //   type of the object
+  bool isOppositeSign() {
+    QualType OppositeSignTy;
+    if (To->isUnsignedIntegerOrEnumerationType())
+      OppositeSignTy = Ctx.getCorrespondingSignedType(To);
+    else if (To->isSignedIntegerOrEnumerationType())
+      OppositeSignTy = Ctx.getCorrespondingUnsignedType(To);
+    return From == OppositeSignTy;
+  }
+};
+
+class StrictAliasingChecker : public Checker<check::PreStmt<UnaryOperator>,
+                                             check::PreStmt<ArraySubscriptExpr>,
+                                             check::PreStmt<MemberExpr>> {
+  BugType BT{this, "Strict Aliasing Rule",
+             "Access Violation through unallowed type."};
+
+public:
+  void checkPreStmt(const MemberExpr *ME, CheckerContext &C) const {
+    checkAccess(ME->getBase(), ME->getBase()->getType()->getPointeeType(), C);
+  }
+
+  void checkPreStmt(const ArraySubscriptExpr *ASE, CheckerContext &C) const {
+    checkAccess(ASE->getBase(), ASE->getType(), C);
+  }
+
+  void checkPreStmt(const UnaryOperator *UO, CheckerContext &C) const {
+    // Handle indirection operator '*' only.
+    if (UO->getOpcode() == UO_Deref)
+      checkAccess(UO->getSubExpr(), UO->getType(), C);
+  }
+
+private:
+  void checkAccess(const Expr *OrigEx, QualType AliasedTy,
+                   CheckerContext &C) const {
+    AliasedTy = getCanonicalUnqualifiedType(AliasedTy);
+    // TODO: Handle this case in a proper way, if any.
+    if (AliasedTy.isNull())
+      return;
+
+    const QualType OrigTy = getOriginalType(OrigEx, C);
+    // TODO: Handle this case in a proper way, if any.
+    if (OrigTy.isNull())
+      return;
+
+    if (!AccessInferrer::canAccess(OrigTy, AliasedTy, C.getASTContext()))
+      reportBug(C, OrigTy, AliasedTy);
+  }
+
+  // FIXME: Probably, we should have such function in QualType class.
+  // Existing `T->getCanonicalTypeUnqualified()` does not return unqualified
+  // type which, apparently, is expected.
+  QualType getCanonicalUnqualifiedType(QualType T) const {
+    if (!T.isNull()) {
+      T = T->getCanonicalTypeUnqualified();
+      T.removeLocalFastQualifiers();
+    }
+    return T;
+  }
+
+  QualType getOriginalType(const Expr *OrigEx, CheckerContext &C) const {
+    QualType T;
+
+    SVal V = C.getSVal(OrigEx->IgnoreParens());
+    if (V.isUnknownOrUndef())
+      return T;
+
+    if (auto MRV = V.getAs<loc::MemRegionVal>()) {
+      // Unwrap ElementRegion and CXXBaseObjectRegion nesting to get the base
+      // region.
+      const MemRegion *Base = MRV->getRegion()->StripCasts(true);
+      // Get type of the original declaration.
+      if (const auto *VR = dyn_cast<VarRegion>(Base))
+        T = VR->getValueType();
+      else if (const auto *SR = dyn_cast<SymbolicRegion>(Base))
+        T = SR->getSymbol()->getType()->getPointeeType();
+      // TODO: Support other regions in a proper way if would need.
+    }
+
+    // Get type from other types.
+    // NOTE: This is very inaccurate but it's better than NULL though.
+    if (T.isNull())
+      T = V.getType(C.getASTContext());
+
+    return getCanonicalUnqualifiedType(T);
+  }
+
+  void reportBug(CheckerContext &C, QualType From, QualType To) const {
+    SmallString<256> Buf;
+    llvm::raw_svector_ostream OS(Buf);
+    OS << "Undefined behavior. Attempting to access the stored value of type ";
+    OS << "'" << From.getAsString() << "'";
+    OS << " through unallowed type ";
+    OS << "'" << To.getAsString() << "'.";
+
+    ExplodedNode *Node = C.generateNonFatalErrorNode();
+    auto Report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), Node);
+    C.emitReport(std::move(Report));
+  }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerStrictAliasingChecker(CheckerManager &CM) {
+  CM.registerChecker<StrictAliasingChecker>();
+}
+
+bool ento::shouldRegisterStrictAliasingChecker(const CheckerManager &CM) {
+  const LangOptions &LO = CM.getLangOpts();
+  const bool IsStrictAliasing = !CM.getCodeGenOpts().RelaxedAliasing;
+  // Support from C++20 for now.
+  // TODO: Support C++98/11/14/17. Shall we?
+  // TODO: Support C.
+  return IsStrictAliasing && (LO.CPlusPlus20 || LO.CPlusPlus2b);
+}
Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -105,6 +105,7 @@
   StdLibraryFunctionsChecker.cpp
   STLAlgorithmModeling.cpp
   StreamChecker.cpp
+  StrictAliasingChecker.cpp
   StringChecker.cpp
   Taint.cpp
   TaintTesterChecker.cpp
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -298,6 +298,12 @@
   Dependencies<[PthreadLockBase]>,
   Documentation<HasAlphaDocumentation>;
 
+def StrictAliasingChecker : Checker<"StrictAliasing">,
+  HelpText<"Check conformity with Strict Alising Rule. Check an access to the "
+           "stored value through a glvalue whose type is not allowed by "
+           "the Standard. ([basic.lval])">,
+  Documentation<HasAlphaDocumentation>;
+
 } // end "alpha.core"
 
 //===----------------------------------------------------------------------===//
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to