[clang] [analyzer] Add alpha.cplusplus.PlacementSelfCopy checker (PR #233525)

via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 24 07:53:24 PDT 2026


================
@@ -0,0 +1,438 @@
+//===- PlacementSelfCopyChecker.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 placement new expressions whose initializer
+// reads a part of the target storage that has already been overwritten, e.g.
+// `::new (p) T(*p)`. A copy or move constructor that is not inlined is
+// reported whenever its source is the target storage.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ParentMap.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"
+
+using namespace clang;
+using namespace ento;
+
+using PlacementKey = std::pair<const CXXNewExpr *, const StackFrame *>;
+REGISTER_MAP_WITH_PROGRAMSTATE(PlacementTargets, PlacementKey,
+                               const MemRegion *)
+REGISTER_SET_WITH_PROGRAMSTATE(SelfSourcedTargets, const MemRegion *)
+REGISTER_SET_WITH_PROGRAMSTATE(OverwrittenParts, const MemRegion *)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingSelfCopy, const MemRegion *)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ReadOverwritten, const MemRegion *)
+
+namespace {
+class PlacementSelfCopyChecker
+    : public Checker<check::PostCall, check::PreCall, check::Bind,
+                     check::Location, check::PostStmt<CXXNewExpr>> {
+  const BugType BT{this, "Placement new initialized from its own storage",
+                   categories::MemoryError};
+  const BugType ReadBT{this, "Placement new reads overwritten storage",
+                       categories::MemoryError};
+
+  void reportRead(const Expr *E, const MemRegion *Overwritten,
+                  CheckerContext &C) const;
+  void reportSelfCopy(const Expr *Arg, CheckerContext &C) const;
+  void reportPendingSelfCopy(const CallEvent &Call, CheckerContext &C) const;
+  void checkOverwrittenRead(const Stmt *S, const Expr *E,
+                            const MemRegion *Source, CheckerContext &C) const;
+
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+  void checkBind(SVal Loc, SVal Val, const Stmt *S, bool AtDeclInit,
+                 CheckerContext &C) const;
+  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+                     CheckerContext &C) const;
+  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+};
+} // namespace
+
+namespace {
+struct Extent {
+  const MemRegion *Base;
+  int64_t Begin;
+  std::optional<int64_t> End;
+};
+} // namespace
+
+static std::optional<Extent> getExtent(const MemRegion *R, ASTContext &Ctx) {
+  RegionOffset Offset = R->getAsOffset();
+  if (!Offset.isValid() || Offset.hasSymbolicOffset())
+    return std::nullopt;
+  Extent E{Offset.getRegion(), Offset.getOffset(), std::nullopt};
+  if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+    QualType Ty = TR->getValueType();
+    if (!Ty->isIncompleteType())
+      E.End = E.Begin + Ctx.getTypeSize(Ty);
+  }
+  return E;
+}
+
+static bool overlaps(const MemRegion *A, const MemRegion *B, ASTContext &Ctx) {
+  if (A == B || A->isSubRegionOf(B) || B->isSubRegionOf(A))
+    return true;
+  std::optional<Extent> EA = getExtent(A, Ctx), EB = getExtent(B, Ctx);
+  if (!EA || !EB || EA->Base != EB->Base)
+    return false;
+  return (!EB->End || EA->Begin < *EB->End) &&
+         (!EA->End || EB->Begin < *EA->End);
+}
+
+static bool isWithin(const MemRegion *R, const MemRegion *Container,
+                     ASTContext &Ctx) {
+  if (R == Container || R->isSubRegionOf(Container))
+    return true;
+  std::optional<Extent> ER = getExtent(R, Ctx), EC = getExtent(Container, Ctx);
+  if (!ER || !EC || ER->Base != EC->Base || ER->Begin < EC->Begin)
+    return false;
+  return !EC->End || (ER->End && *ER->End <= *EC->End);
+}
+
+static bool isThisBased(const Expr *E) {
+  while (E) {
+    E = E->IgnoreParenCasts();
+    if (isa<CXXThisExpr>(E))
+      return true;
+    if (const auto *ME = dyn_cast<MemberExpr>(E))
+      E = ME->getBase();
+    else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E))
+      E = ASE->getBase();
+    else if (const auto *BO = dyn_cast<BinaryOperator>(E);
+             BO && BO->isAssignmentOp())
+      E = BO->getLHS();
+    else if (const auto *UO = dyn_cast<UnaryOperator>(E);
+             UO && UO->getOpcode() == UO_Deref)
+      E = UO->getSubExpr();
+    else
+      return false;
+  }
+  return false;
+}
+
+static const MemRegion *findSelfSourcedTarget(ProgramStateRef State,
+                                              const MemRegion *R,
+                                              CheckerContext &C) {
+  for (const MemRegion *Target : State->get<SelfSourcedTargets>())
+    if (isWithin(R, Target, C.getASTContext()))
+      return Target;
+  return nullptr;
+}
+
+static const MemRegion *findOverwritten(ProgramStateRef State,
+                                        const MemRegion *R, CheckerContext &C) {
+  for (const MemRegion *W : State->get<OverwrittenParts>())
+    if (overlaps(R, W, C.getASTContext()))
+      return W;
+  return nullptr;
+}
+
+// CSA binds an init list only after evaluating all elements; recompute order.
+static const MemRegion *walkInitList(PlacementKey Key, const MemRegion *Target,
----------------
mark-zilber wrote:

Refactored whole code.

https://github.com/llvm/llvm-project/pull/233525


More information about the cfe-commits mailing list