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
