Author: Zeyi Xu
Date: 2026-05-28T13:17:52+08:00
New Revision: 846bfd77ddc44ef76882fd165fa4d5b68b760456

URL: 
https://github.com/llvm/llvm-project/commit/846bfd77ddc44ef76882fd165fa4d5b68b760456
DIFF: 
https://github.com/llvm/llvm-project/commit/846bfd77ddc44ef76882fd165fa4d5b68b760456.diff

LOG: [LifetimeSafety] Avoid assert on variadic placement new (#199588)

Avoid assuming that a placement allocation function has a second
`ParmVarDecl` before checking whether that parameter is `void*`.
Variadic `operator new(size_t, ...)` can have a placement argument
matched by the ellipsis instead.

As of AI Usage: Codex is used to help rephrase part of the new comments.

Closes https://github.com/llvm/llvm-project/issues/199584

Added: 
    

Modified: 
    clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
    clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
    clang/test/Sema/warn-lifetime-safety.cpp

Removed: 
    


################################################################################
diff  --git 
a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
index 943bdd7199bf3..0b6d8c4fc026e 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h
@@ -76,6 +76,8 @@ class FactsGenerator : public 
ConstStmtVisitor<FactsGenerator> {
 
   void handlePointerArithmetic(const BinaryOperator *BO);
 
+  void handlePlacementNew(const CXXNewExpr *NE, OriginList *NewList);
+
   void handleCXXCtorInitializer(const CXXCtorInitializer *CII);
 
   void handleLifetimeEnds(const CFGLifetimeEnds &LifetimeEnds);

diff  --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 323802c6a88d5..b34a04f1f3bda 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -631,38 +631,48 @@ void FactsGenerator::VisitArraySubscriptExpr(const 
ArraySubscriptExpr *ASE) {
       Dst->getOuterOriginID(), Src->getOuterOriginID(), /*Kill=*/true));
 }
 
+void FactsGenerator::handlePlacementNew(const CXXNewExpr *NE,
+                                        OriginList *NewList) {
+  // Model only the standard single-argument placement new form, where the
+  // placement argument corresponds to a void* allocation-function parameter.
+  // Other placement forms, such as std::nothrow, are not modeled as providing
+  // storage for the returned pointer.
+  if (NE->getNumPlacementArgs() != 1)
+    return;
+
+  const FunctionDecl *OperatorNew = NE->getOperatorNew();
+  if (OperatorNew->getNumParams() <= 1)
+    return;
+
+  const auto *Arg =
+      OperatorNew->getParamDecl(1)->getType()->getAs<PointerType>();
+  if (!Arg || !Arg->isVoidPointerType())
+    return;
+
+  // Use the placement argument before the implicit conversion to void*, so
+  // inner origins are still available.
+  const Expr *PlacementArg = NE->getPlacementArg(0);
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg);
+      ICE && ICE->getCastKind() == CK_BitCast &&
+      PlacementArg->getType()->isVoidPointerType())
+    PlacementArg = ICE->getSubExpr();
+  OriginList *PlacementList = getOriginsList(*PlacementArg);
+  // FIXME: General placement arguments need separate handling to overwrite
+  // the right origins.
+
+  // The pointer returned by placement new comes from the placement
+  // argument.
+  if (PlacementList)
+    CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
+        NewList->getOuterOriginID(), PlacementList->getOuterOriginID(), true));
+}
+
 void FactsGenerator::VisitCXXNewExpr(const CXXNewExpr *NE) {
   OriginList *NewList = getOriginsList(*NE);
   const Expr *Init = NE->getInitializer();
 
-  // Check if we have a placement new where the second argument is void*, to
-  // avoid flowing from non-pointer parameters, such as std::nothrow.
-  // And that the placement parameter num is 1,
-  // that is to mostly limit to standard library placement new.
   if (NE->getNumPlacementArgs() == 1) {
-    if (const auto *Arg = NE->getOperatorNew()
-                              ->getParamDecl(1)
-                              ->getType()
-                              ->getAs<PointerType>();
-        Arg && Arg->isVoidPointerType()) {
-      // Use the placement argument before the implicit conversion to void*, so
-      // inner origins are still available.
-      const Expr *PlacementArg = NE->getPlacementArg(0);
-      if (const auto *ICE = dyn_cast<ImplicitCastExpr>(PlacementArg);
-          ICE && ICE->getCastKind() == CK_BitCast &&
-          PlacementArg->getType()->isVoidPointerType())
-        PlacementArg = ICE->getSubExpr();
-      OriginList *PlacementList = getOriginsList(*PlacementArg);
-      // FIXME: General placement arguments need separate handling to overwrite
-      // the right origins.
-
-      // The pointer returned by placement new comes from the placement
-      // argument.
-      if (PlacementList)
-        CurrentBlockFacts.push_back(FactMgr.createFact<OriginFlowFact>(
-            NewList->getOuterOriginID(), PlacementList->getOuterOriginID(),
-            true));
-    }
+    handlePlacementNew(NE, NewList);
   } else {
     const Loan *L = createLoan(FactMgr, NE);
     CurrentBlockFacts.push_back(

diff  --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index ae9d5d08e358b..87a9b6c330f73 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2982,6 +2982,17 @@ void placement_new_heap_then_delete_use_after_free() {
   (void)*p;                  // expected-note {{later used here}}
 }
 
+struct PlacementArg {};
+
+struct VariadicPlacementNew {
+  void *operator new(std::size_t, ...);
+};
+
+void variadic_placement_new() {
+  PlacementArg arg;
+  (void)new (arg) VariadicPlacementNew;
+}
+
 int* foo(int* x [[clang::lifetimebound]], int* y [[clang::lifetimebound]]);
 
 void placement_new_delete_result_of_lifetimebound_call() {


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

Reply via email to