================
@@ -0,0 +1,339 @@
+//===- DestroyedAsWrongTypeChecker.cpp --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker that finds storage reused by placement new for an object
+// of a different type and then destroyed or deleted as the original type
+// (C++ [basic.life]p11, [expr.delete]p3).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct WrongDestruction {
+  QualType Placed;
+  const CXXRecordDecl *DtorClass;
+  const CXXDeleteExpr *Delete;
+
+  bool operator==(const WrongDestruction &X) const {
+    return Placed == X.Placed && DtorClass == X.DtorClass && Delete == 
X.Delete;
+  }
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.Add(Placed);
+    ID.AddPointer(DtorClass);
+    ID.AddPointer(Delete);
+  }
+};
+} // namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(PlacedObjectTypes, const MemRegion *, QualType)
+REGISTER_SET_WITH_PROGRAMSTATE(NewAllocatedObjects, const MemRegion *)
+REGISTER_MAP_WITH_PROGRAMSTATE(PendingDestructions, const MemRegion *,
+                               WrongDestruction)
+
+namespace {
+class DestroyedAsWrongTypeChecker
+    : public Checker<check::PostStmt<CXXNewExpr>, check::PreCall,
+                     check::PostCall, check::DeadSymbols> {
+  const BugType BT{this, "Object destroyed as a type it no longer has",
+                   categories::MemoryError};
+
+  void reportBug(const MemRegion *R, const WrongDestruction &W,
+                 CheckerContext &C) const;
+  void reportOrDefer(const MemRegion *R, const WrongDestruction &W,
+                     CheckerContext &C) const;
+
+public:
+  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+};
+} // namespace
+
+// An array is keyed by its first element, the object its destructor gets.
+static const MemRegion *getStorageRegion(SVal V, CheckerContext &C) {
+  const MemRegion *R = V.getAsRegion();
+  if (!R)
+    return nullptr;
+  R = R->StripCasts();
+  ASTContext &Ctx = C.getASTContext();
+  while (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+    const ArrayType *AT = Ctx.getAsArrayType(TR->getValueType());
+    if (!AT)
+      break;
+    R = C.getStoreManager().getRegionManager().getElementRegion(
+        AT->getElementType(), C.getSValBuilder().makeZeroArrayIndex(), TR, 
Ctx);
+  }
+  return R;
+}
+
+// Storage obtained as raw memory has no original object to be replaced.
+static bool holdsOriginalObject(ProgramStateRef State, const MemRegion *R) {
+  if (State->contains<NewAllocatedObjects>(R))
+    return true;
+  const auto *TR = dyn_cast<TypedValueRegion>(R);
+  if (!TR)
+    return false;
+  // Placement new into a union starts the lifetime of one of its members.
+  const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl();
+  return RD && !RD->isUnion() && RD->hasNonTrivialDestructor();
+}
+
+static bool isSameRecord(QualType T, const CXXRecordDecl *RD) {
+  const CXXRecordDecl *TD = T->getAsCXXRecordDecl();
+  return TD && TD->getCanonicalDecl() == RD->getCanonicalDecl();
+}
+
+static QualType getStorageType(const MemRegion *R) {
+  if (const auto *TR = dyn_cast<TypedValueRegion>(R))
+    return TR->getValueType();
+  if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+    return SR->getPointeeStaticType();
+  return {};
+}
+
+// A field and a byte offset from its parent are different regions.
+static bool isSameLocation(const MemRegion *A, const MemRegion *B) {
+  RegionOffset OA = A->getAsOffset();
+  RegionOffset OB = B->getAsOffset();
+  return OA.isValid() && OB.isValid() && !OA.hasSymbolicOffset() &&
+         !OB.hasSymbolicOffset() && OA.getRegion() == OB.getRegion() &&
+         OA.getOffset() == OB.getOffset();
+}
+
+static ProgramStateRef forgetPlacedTypes(ProgramStateRef State,
+                                         const MemRegion *R) {
+  for (const MemRegion *Key :
+       llvm::make_first_range(State->get<PlacedObjectTypes>()))
+    if (Key == R || Key->isSubRegionOf(R) || isSameLocation(Key, R))
----------------
NagyDonat wrote:

At first I thought that `Key == R` is just a performance optimization, but in 
fact it is significant, because `isSameLocation(Key, R)` can return false when 
the offsets are symbolic.

Please move this equality check into `isSameLocation` (as an `if (A == B) 
return true;` check at the beginning) to make the name of that function more 
accurate.

https://github.com/llvm/llvm-project/pull/237801
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to