[clang] Fix unique_ptr aggregate initialization false positives (PR #155131)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 26 15:51:09 PDT 2025
https://github.com/vidur2 updated https://github.com/llvm/llvm-project/pull/155131
>From f903652135758b6a7a20d15565177304add16e2c Mon Sep 17 00:00:00 2001
From: vidur2 <vmod2005 at gmail.com>
Date: Thu, 14 Aug 2025 17:28:36 -0400
Subject: [PATCH 1/4] initial malloc change check
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index efb980962e811..40d13b4597d79 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3074,6 +3074,42 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
(*PostFN)(this, C.getState(), Call, C);
return;
}
+
+ ProgramStateRef State = C.getState();
+
+ if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ // Ensure we are constructing a concrete object/subobject.
+ if (const MemRegion *ObjUnderConstr = Ctor->getCXXThisVal().getAsRegion()) {
+ ProgramStateRef NewState = State;
+
+ for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
+ SVal ArgV = Call.getArgSVal(I);
+
+ SymbolRef Sym = ArgV.getAsSymbol();
+ if (!Sym)
+ continue;
+
+ // Look up current ref-state for this symbol in the RegionState map.
+ if (const RefState *RS = State->get<RegionState>(Sym)) {
+ // Only re-label symbols that are still owned allocations from C++ new/new[].
+ if (RS->isAllocated() &&
+ (RS->getAllocationFamily().Kind == AF_CXXNew ||
+ RS->getAllocationFamily().Kind == AF_CXXNewArray)) {
+
+ // Mark as Relinquished at the constructor site: ownership moves
+ // into the constructed subobject. Pass the ctor's origin expr as
+ // the statement associated with this transition.
+ NewState = NewState->set<RegionState>(
+ Sym, RefState::getRelinquished(RS->getAllocationFamily(),
+ Ctor->getOriginExpr()));
+ }
+ }
+ }
+
+ if (NewState != State)
+ C.addTransition(NewState);
+ }
+ }
}
void MallocChecker::checkPreCall(const CallEvent &Call,
>From debfddd77c6f988cf50600f22ffad8a197d41298 Mon Sep 17 00:00:00 2001
From: vidur2 <vmod2005 at gmail.com>
Date: Thu, 14 Aug 2025 18:01:23 -0400
Subject: [PATCH 2/4] added tests
---
clang/test/Analysis/NewDeleteLeaks.cpp | 140 +++++++++++++++++++++++++
1 file changed, 140 insertions(+)
diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp
index b2bad7e76fad0..3e54aa900f5cc 100644
--- a/clang/test/Analysis/NewDeleteLeaks.cpp
+++ b/clang/test/Analysis/NewDeleteLeaks.cpp
@@ -14,6 +14,8 @@
#include "Inputs/system-header-simulator-for-malloc.h"
+#include <utility>
+
//===----------------------------------------------------------------------===//
// Report for which we expect NoOwnershipChangeVisitor to add a new note.
//===----------------------------------------------------------------------===//
@@ -218,3 +220,141 @@ void caller() {
(void)n;
} // no-warning: No potential memory leak here, because that's been already reported.
} // namespace symbol_reaper_lifetime
+
+
+// RUN: %clang_analyze_cc1 -std=c++20 -analyzer-checker=cplusplus.NewDeleteLeaks -verify %s
+
+
+// Minimal RAII class that properly deletes its pointer.
+class Bar {
+public:
+ explicit Bar(int *ptr) : ptr_(ptr) {}
+ ~Bar() {
+ if (ptr_) {
+ delete ptr_;
+ ptr_ = nullptr;
+ }
+ }
+
+ Bar(const Bar &) = delete;
+ Bar &operator=(const Bar &) = delete;
+
+ Bar(Bar &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; }
+ Bar &operator=(Bar &&other) noexcept {
+ if (this != &other) {
+ delete ptr_;
+ ptr_ = other.ptr_;
+ other.ptr_ = nullptr;
+ }
+ return *this;
+ }
+
+ int operator*() const { return *ptr_; }
+
+private:
+ int *ptr_;
+};
+
+// Factory returning a prvalue Bar that owns a freshly allocated int.
+static Bar make_bar(int v) { return Bar(new int(v)); }
+
+struct Foo {
+ Bar a;
+ Bar b;
+};
+
+struct FooWithConstructor {
+ Bar a;
+ Bar b;
+ FooWithConstructor(Bar &&original_a, Bar &&original_b)
+ : a(std::move(original_a)), b(std::move(original_b)) {}
+};
+
+//===----------------------------------------------------------------------===//
+// No-false-positive regression tests: these must be silent
+//===----------------------------------------------------------------------===//
+
+namespace prvalue_aggregate_transfer {
+
+void ok_aggregate_from_factory() {
+ Foo foo = {make_bar(1), make_bar(2)}; // expected-no-diagnostics
+}
+
+void ok_aggregate_from_temporary_exprs() {
+ Foo foo = {Bar(new int(1)), Bar(new int(2))}; // expected-no-diagnostics
+}
+
+void ok_ctor_from_factory_rvalues() {
+ FooWithConstructor foo = {make_bar(1), make_bar(2)}; // expected-no-diagnostics
+}
+
+} // namespace prvalue_aggregate_transfer
+
+//===----------------------------------------------------------------------===//
+// True-positive regression tests: these should still warn
+//===----------------------------------------------------------------------===//
+
+class BarNoDelete {
+public:
+ explicit BarNoDelete(int *ptr) : ptr_(ptr) {}
+ ~BarNoDelete() {} // intentionally missing delete -> leak
+
+ BarNoDelete(const BarNoDelete &) = delete;
+ BarNoDelete &operator=(const BarNoDelete &) = delete;
+
+ BarNoDelete(BarNoDelete &&other) noexcept : ptr_(other.ptr_) {
+ other.ptr_ = nullptr;
+ }
+ BarNoDelete &operator=(BarNoDelete &&other) noexcept {
+ if (this != &other) {
+ // no delete of old ptr_ on purpose
+ ptr_ = other.ptr_;
+ other.ptr_ = nullptr;
+ }
+ return *this;
+ }
+
+private:
+ int *ptr_;
+};
+
+static BarNoDelete make_bar_nd(int v) { return BarNoDelete(new int(v)); }
+
+struct FooND {
+ BarNoDelete a;
+ BarNoDelete b;
+};
+
+namespace prvalue_aggregate_positive {
+
+void leak_aggregate_from_factory() {
+ FooND f = {make_bar_nd(1), make_bar_nd(2)};
+ // expected-warning at -1 {{Potential memory leak}}
+}
+
+void leak_direct_member() {
+ BarNoDelete b(new int(3));
+ // expected-warning at -1 {{Potential memory leak}}
+}
+
+} // namespace prvalue_aggregate_positive
+
+//===----------------------------------------------------------------------===//
+// Guard tests: neighboring behaviors that must remain intact
+// These ensure we didn't weaken unrelated diagnostics (mismatch/double-delete).
+//===----------------------------------------------------------------------===//
+
+namespace guards {
+
+void mismatch_array_delete() {
+ int *p = new int[4];
+ delete p; // expected-warning {{mismatched deallocation: 'delete' should be 'delete[]'}}
+}
+
+void double_delete() {
+ int *p = new int(1);
+ delete p;
+ delete p; // expected-warning {{Attempt to free released memory}}
+}
+
+} // namespace guards
>From 499ed6745e77a09860fa791063c346eeb31cd478 Mon Sep 17 00:00:00 2001
From: vidur2 <vmod2005 at gmail.com>
Date: Thu, 14 Aug 2025 18:07:25 -0400
Subject: [PATCH 3/4] ran formatter
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 261 +++++++++---------
1 file changed, 125 insertions(+), 136 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 40d13b4597d79..eda1c73b6e029 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -231,12 +231,15 @@ class RefState {
LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
switch (K) {
-#define CASE(ID) case ID: OS << #ID; break;
- CASE(Allocated)
- CASE(AllocatedOfSizeZero)
- CASE(Released)
- CASE(Relinquished)
- CASE(Escaped)
+#define CASE(ID) \
+ case ID: \
+ OS << #ID; \
+ break;
+ CASE(Allocated)
+ CASE(AllocatedOfSizeZero)
+ CASE(Released)
+ CASE(Relinquished)
+ CASE(Escaped)
}
}
@@ -304,8 +307,7 @@ struct ReallocPair {
ID.AddPointer(ReallocatedSym);
}
bool operator==(const ReallocPair &X) const {
- return ReallocatedSym == X.ReallocatedSym &&
- Kind == X.Kind;
+ return ReallocatedSym == X.ReallocatedSym && Kind == X.Kind;
}
};
@@ -422,27 +424,28 @@ class MallocChecker
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &Call,
+ CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
- bool Assumption) const;
+ bool Assumption) const;
void checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const;
ProgramStateRef checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const;
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
ProgramStateRef checkConstPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const;
- void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const override;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
StringRef getDebugTag() const override { return "MallocChecker"; }
@@ -789,9 +792,10 @@ class MallocChecker
///
/// We assume that pointers do not escape through calls to system functions
/// not handled by this checker.
- bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
- ProgramStateRef State,
- SymbolRef &EscapingSymbol) const;
+ bool
+ mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
[[nodiscard]] ProgramStateRef
@@ -1253,9 +1257,8 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
NonLoc ZeroFlag = C.getSValBuilder()
.makeIntVal(*KernelZeroFlagVal, FlagsEx->getType())
.castAs<NonLoc>();
- SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
- Flags, ZeroFlag,
- FlagsEx->getType());
+ SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(
+ State, BO_And, Flags, ZeroFlag, FlagsEx->getType());
if (MaskedFlagsUC.isUnknownOrUndef())
return std::nullopt;
DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
@@ -1485,7 +1488,8 @@ void MallocChecker::checkGMemdup(ProgramStateRef State, const CallEvent &Call,
void MallocChecker::checkGMallocN(ProgramStateRef State, const CallEvent &Call,
CheckerContext &C) const {
SVal Init = UndefinedVal();
- SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ SVal TotalSize =
+ evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
State = MallocMemAux(C, Call, TotalSize, Init, State,
AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(C, Call, 0, State);
@@ -1497,7 +1501,8 @@ void MallocChecker::checkGMallocN0(ProgramStateRef State, const CallEvent &Call,
CheckerContext &C) const {
SValBuilder &SB = C.getSValBuilder();
SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
- SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ SVal TotalSize =
+ evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
State = MallocMemAux(C, Call, TotalSize, Init, State,
AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(C, Call, 0, State);
@@ -2061,8 +2066,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
/// Checks if the previous call to free on the given symbol failed - if free
/// failed, returns true. Also, returns the corresponding return value symbol.
-static bool didPreviousFreeFail(ProgramStateRef State,
- SymbolRef Sym, SymbolRef &RetStatusSymbol) {
+static bool didPreviousFreeFail(ProgramStateRef State, SymbolRef Sym,
+ SymbolRef &RetStatusSymbol) {
const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);
if (Ret) {
assert(*Ret && "We should not store the null return symbol");
@@ -2318,8 +2323,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// Check if the memory location being freed is the actual location
// allocated, or an offset.
RegionOffset Offset = R->getAsOffset();
- if (Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
+ if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
Offset.getOffset() != 0) {
const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
HandleOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
@@ -2365,9 +2369,8 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// Normal free.
if (Hold)
- return State->set<RegionState>(SymBase,
- RefState::getRelinquished(Family,
- ParentExpr));
+ return State->set<RegionState>(
+ SymBase, RefState::getRelinquished(Family, ParentExpr));
return State->set<RegionState>(SymBase,
RefState::getReleased(Family, ParentExpr));
@@ -2606,12 +2609,12 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,
os << " allocated by " << AllocOs.str();
os << " should be deallocated by ";
- printExpectedDeallocName(os, RS->getAllocationFamily());
+ printExpectedDeallocName(os, RS->getAllocationFamily());
- if (printMemFnName(DeallocOs, C, DeallocExpr))
- os << ", not " << DeallocOs.str();
+ if (printMemFnName(DeallocOs, C, DeallocExpr))
+ os << ", not " << DeallocOs.str();
- printOwnershipTakesList(os, C, DeallocExpr);
+ printOwnershipTakesList(os, C, DeallocExpr);
}
auto R = std::make_unique<PathSensitiveBugReport>(
@@ -2648,8 +2651,7 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
assert(MR && "Only MemRegion based symbols can have offset free errors");
RegionOffset Offset = MR->getAsOffset();
- assert((Offset.isValid() &&
- !Offset.hasSymbolicOffset() &&
+ assert((Offset.isValid() && !Offset.hasSymbolicOffset() &&
Offset.getOffset() != 0) &&
"Only symbols with a valid offset can have offset free errors");
@@ -2658,11 +2660,8 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
os << "Argument to ";
if (!printMemFnName(os, C, DeallocExpr))
os << "deallocator";
- os << " is offset by "
- << offsetBytes
- << " "
- << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
- << " from the start of ";
+ os << " is offset by " << offsetBytes << " "
+ << ((abs(offsetBytes) > 1) ? "bytes" : "byte") << " from the start of ";
if (AllocExpr && printMemFnName(AllocNameOs, C, AllocExpr))
os << "memory allocated by " << AllocNameOs.str();
else
@@ -2892,8 +2891,8 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
// Record the info about the reallocated symbol so that we could properly
// process failed reallocation.
- stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
- ReallocPair(FromPtr, Kind));
+ stateRealloc =
+ stateRealloc->set<ReallocPairs>(ToPtr, ReallocPair(FromPtr, Kind));
// The reallocated symbol should stay alive for as long as the new symbol.
C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
return stateRealloc;
@@ -2951,8 +2950,7 @@ MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
// Allocation node, is the last node in the current or parent context in
// which the symbol was tracked.
const LocationContext *NContext = N->getLocationContext();
- if (NContext == LeakContext ||
- NContext->isParentOf(LeakContext))
+ if (NContext == LeakContext || NContext->isParentOf(LeakContext))
AllocNode = N;
N = N->pred_empty() ? nullptr : *(N->pred_begin());
}
@@ -2988,9 +2986,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics();
if (AllocationStmt)
- LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
- C.getSourceManager(),
- AllocNode->getLocationContext());
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ AllocationStmt, C.getSourceManager(), AllocNode->getLocationContext());
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
@@ -3012,8 +3009,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
}
void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const
-{
+ CheckerContext &C) const {
ProgramStateRef state = C.getState();
RegionStateTy OldRS = state->get<RegionState>();
RegionStateTy::Factory &F = state->get_context<RegionState>();
@@ -3031,8 +3027,7 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
if (RS == OldRS) {
// We shouldn't have touched other maps yet.
- assert(state->get<ReallocPairs>() ==
- C.getState()->get<ReallocPairs>());
+ assert(state->get<ReallocPairs>() == C.getState()->get<ReallocPairs>());
assert(state->get<FreeReturnValue>() ==
C.getState()->get<FreeReturnValue>());
return;
@@ -3091,7 +3086,8 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
// Look up current ref-state for this symbol in the RegionState map.
if (const RefState *RS = State->get<RegionState>(Sym)) {
- // Only re-label symbols that are still owned allocations from C++ new/new[].
+ // Only re-label symbols that are still owned allocations from C++
+ // new/new[].
if (RS->isAllocated() &&
(RS->getAllocationFamily().Kind == AF_CXXNew ||
RS->getAllocationFamily().Kind == AF_CXXNewArray)) {
@@ -3201,8 +3197,7 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
}
}
-void MallocChecker::checkPreStmt(const ReturnStmt *S,
- CheckerContext &C) const {
+void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
checkEscapeOnReturn(S, C);
}
@@ -3234,7 +3229,7 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
if (const MemRegion *MR = RetVal.getAsRegion())
if (isa<FieldRegion, ElementRegion>(MR))
if (const SymbolicRegion *BMR =
- dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
+ dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
Sym = BMR->getSymbol();
// Check if we are returning freed memory.
@@ -3254,14 +3249,13 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
return;
ProgramStateRef state = C.getState();
- const BlockDataRegion *R =
- cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
+ const BlockDataRegion *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
auto ReferencedVars = R->referenced_vars();
if (ReferencedVars.empty())
return;
- SmallVector<const MemRegion*, 10> Regions;
+ SmallVector<const MemRegion *, 10> Regions;
const LocationContext *LC = C.getLocationContext();
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
@@ -3273,8 +3267,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
Regions.push_back(VR);
}
- state =
- state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
+ state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
C.addTransition(state);
}
@@ -3331,8 +3324,7 @@ void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
if (const RefState *RS = C.getState()->get<RegionState>(Sym)) {
if (RS->isAllocatedOfSizeZero())
HandleUseZeroAlloc(C, RS->getStmt()->getSourceRange(), Sym);
- }
- else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
+ } else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
HandleUseZeroAlloc(C, S->getSourceRange(), Sym);
}
}
@@ -3349,9 +3341,8 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
// If a symbolic region is assumed to NULL (or another constant), stop tracking
// it - assuming that allocation failed on this path.
-ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
- SVal Cond,
- bool Assumption) const {
+ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SVal Cond,
+ bool Assumption) const {
RegionStateTy RS = state->get<RegionState>();
for (SymbolRef Sym : llvm::make_first_range(RS)) {
// If the symbol is assumed to be NULL, remove it from consideration.
@@ -3376,7 +3367,8 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
if (RS->isReleased()) {
switch (ReallocPair.Kind) {
case OAR_ToBeFreedAfterFailure:
- state = state->set<RegionState>(ReallocSym,
+ state = state->set<RegionState>(
+ ReallocSym,
RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
break;
case OAR_DoNotTrackAfterFailure:
@@ -3394,9 +3386,8 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
}
bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
- const CallEvent *Call,
- ProgramStateRef State,
- SymbolRef &EscapingSymbol) const {
+ const CallEvent *Call, ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const {
assert(Call);
EscapingSymbol = nullptr;
@@ -3506,8 +3497,8 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// Do not warn on pointers passed to 'setbuf' when used with std streams,
// these leaks might be intentional when setting the buffer for stdio.
// http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName =="setbuffer" ||
- FName == "setlinebuf" || FName == "setvbuf") {
+ if (FName == "setbuf" || FName == "setbuffer" || FName == "setlinebuf" ||
+ FName == "setvbuf") {
if (Call->getNumArgs() >= 1) {
const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
@@ -3557,18 +3548,16 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return false;
}
-ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const {
+ProgramStateRef MallocChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
return checkPointerEscapeAux(State, Escaped, Call, Kind,
/*IsConstPointerEscape*/ false);
}
-ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const {
+ProgramStateRef MallocChecker::checkConstPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
// If a const pointer escapes, it may not be freed(), but it could be deleted.
return checkPointerEscapeAux(State, Escaped, Call, Kind,
/*IsConstPointerEscape*/ true);
@@ -3760,61 +3749,61 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
}
Msg = OS.str();
break;
- }
- case AF_None:
- assert(false && "Unhandled allocation family!");
- return nullptr;
- }
+ }
+ case AF_None:
+ assert(false && "Unhandled allocation family!");
+ return nullptr;
+ }
- // Record the stack frame that is _responsible_ for this memory release
- // event. This will be used by the false positive suppression heuristics
- // that recognize the release points of reference-counted objects.
- //
- // Usually (e.g. in C) we say that the _responsible_ stack frame is the
- // current innermost stack frame:
- ReleaseFunctionLC = CurrentLC->getStackFrame();
- // ...but if the stack contains a destructor call, then we say that the
- // outermost destructor stack frame is the _responsible_ one:
- for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {
- if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {
- if (isReferenceCountingPointerDestructor(DD)) {
- // This immediately looks like a reference-counting destructor.
- // We're bad at guessing the original reference count of the
- // object, so suppress the report for now.
- BR.markInvalid(getTag(), DD);
-
- // After report is considered invalid there is no need to proceed
- // futher.
- return nullptr;
- }
-
- // Switch suspection to outer destructor to catch patterns like:
- // (note that class name is distorted to bypass
- // isReferenceCountingPointerDestructor() logic)
- //
- // SmartPointr::~SmartPointr() {
- // if (refcount.fetch_sub(1) == 1)
- // release_resources();
- // }
- // void SmartPointr::release_resources() {
- // free(buffer);
- // }
- //
- // This way ReleaseFunctionLC will point to outermost destructor and
- // it would be possible to catch wider range of FP.
- //
- // NOTE: it would be great to support smth like that in C, since
- // currently patterns like following won't be supressed:
- //
- // void doFree(struct Data *data) { free(data); }
- // void putData(struct Data *data)
- // {
- // if (refPut(data))
- // doFree(data);
- // }
- ReleaseFunctionLC = LC->getStackFrame();
+ // Record the stack frame that is _responsible_ for this memory release
+ // event. This will be used by the false positive suppression heuristics
+ // that recognize the release points of reference-counted objects.
+ //
+ // Usually (e.g. in C) we say that the _responsible_ stack frame is the
+ // current innermost stack frame:
+ ReleaseFunctionLC = CurrentLC->getStackFrame();
+ // ...but if the stack contains a destructor call, then we say that the
+ // outermost destructor stack frame is the _responsible_ one:
+ for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {
+ if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {
+ if (isReferenceCountingPointerDestructor(DD)) {
+ // This immediately looks like a reference-counting destructor.
+ // We're bad at guessing the original reference count of the
+ // object, so suppress the report for now.
+ BR.markInvalid(getTag(), DD);
+
+ // After report is considered invalid there is no need to proceed
+ // futher.
+ return nullptr;
}
+
+ // Switch suspection to outer destructor to catch patterns like:
+ // (note that class name is distorted to bypass
+ // isReferenceCountingPointerDestructor() logic)
+ //
+ // SmartPointr::~SmartPointr() {
+ // if (refcount.fetch_sub(1) == 1)
+ // release_resources();
+ // }
+ // void SmartPointr::release_resources() {
+ // free(buffer);
+ // }
+ //
+ // This way ReleaseFunctionLC will point to outermost destructor and
+ // it would be possible to catch wider range of FP.
+ //
+ // NOTE: it would be great to support smth like that in C, since
+ // currently patterns like following won't be supressed:
+ //
+ // void doFree(struct Data *data) { free(data); }
+ // void putData(struct Data *data)
+ // {
+ // if (refPut(data))
+ // doFree(data);
+ // }
+ ReleaseFunctionLC = LC->getStackFrame();
}
+ }
} else if (isRelinquished(RSCurr, RSPrev, S)) {
Msg = "Memory ownership is transferred";
@@ -3828,13 +3817,13 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {
// Is it possible to fail two reallocs WITHOUT testing in between?
assert((!FailedReallocSymbol || FailedReallocSymbol == sym) &&
- "We only support one failed realloc at a time.");
+ "We only support one failed realloc at a time.");
BR.markInteresting(sym);
FailedReallocSymbol = sym;
}
}
- // We are in a special mode if a reallocation failed later in the path.
+ // We are in a special mode if a reallocation failed later in the path.
} else if (Mode == ReallocationFailed) {
assert(FailedReallocSymbol && "No symbol to look for.");
@@ -3903,8 +3892,8 @@ namespace clang {
namespace ento {
namespace allocation_state {
-ProgramStateRef
-markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
+ const Expr *Origin) {
AllocationFamily Family(AF_InnerBuffer);
return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
}
>From 0d99b6e0751b821657b275b45385417c91d5b8b9 Mon Sep 17 00:00:00 2001
From: vidur2 <vmod2005 at gmail.com>
Date: Tue, 26 Aug 2025 12:12:39 -0400
Subject: [PATCH 4/4] changed according to initial spot check review
---
.../StaticAnalyzer/Checkers/MallocChecker.cpp | 138 ++++++++++--------
clang/test/Analysis/NewDeleteLeaks.cpp | 26 +++-
2 files changed, 97 insertions(+), 67 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index eda1c73b6e029..1c45ecaadcc56 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -307,7 +307,8 @@ struct ReallocPair {
ID.AddPointer(ReallocatedSym);
}
bool operator==(const ReallocPair &X) const {
- return ReallocatedSym == X.ReallocatedSym && Kind == X.Kind;
+ return ReallocatedSym == X.ReallocatedSym &&
+ Kind == X.Kind;
}
};
@@ -416,7 +417,7 @@ class MallocChecker
// This last frontend is associated with a single bug type which is not used
// elsewhere and has a different bug category, so it's declared separately.
CheckerFrontendWithBugType TaintedAllocChecker{"Tainted Memory Allocation",
- categories::TaintedData};
+ categories::TaintedData};
using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>;
@@ -424,28 +425,27 @@ class MallocChecker
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const;
- void checkPostObjCMessage(const ObjCMethodCall &Call,
- CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
- bool Assumption) const;
+ bool Assumption) const;
void checkLocation(SVal l, bool isLoad, const Stmt *S,
- CheckerContext &C) const;
+ CheckerContext &C) const;
ProgramStateRef checkPointerEscape(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind) const;
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
ProgramStateRef checkConstPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const;
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
StringRef getDebugTag() const override { return "MallocChecker"; }
@@ -792,10 +792,9 @@ class MallocChecker
///
/// We assume that pointers do not escape through calls to system functions
/// not handled by this checker.
- bool
- mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
- ProgramStateRef State,
- SymbolRef &EscapingSymbol) const;
+ bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
[[nodiscard]] ProgramStateRef
@@ -1257,8 +1256,9 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
NonLoc ZeroFlag = C.getSValBuilder()
.makeIntVal(*KernelZeroFlagVal, FlagsEx->getType())
.castAs<NonLoc>();
- SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(
- State, BO_And, Flags, ZeroFlag, FlagsEx->getType());
+ SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
+ Flags, ZeroFlag,
+ FlagsEx->getType());
if (MaskedFlagsUC.isUnknownOrUndef())
return std::nullopt;
DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
@@ -1488,8 +1488,7 @@ void MallocChecker::checkGMemdup(ProgramStateRef State, const CallEvent &Call,
void MallocChecker::checkGMallocN(ProgramStateRef State, const CallEvent &Call,
CheckerContext &C) const {
SVal Init = UndefinedVal();
- SVal TotalSize =
- evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
State = MallocMemAux(C, Call, TotalSize, Init, State,
AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(C, Call, 0, State);
@@ -1501,8 +1500,7 @@ void MallocChecker::checkGMallocN0(ProgramStateRef State, const CallEvent &Call,
CheckerContext &C) const {
SValBuilder &SB = C.getSValBuilder();
SVal Init = SB.makeZeroVal(SB.getContext().CharTy);
- SVal TotalSize =
- evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
+ SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1));
State = MallocMemAux(C, Call, TotalSize, Init, State,
AllocationFamily(AF_Malloc));
State = ProcessZeroAllocCheck(C, Call, 0, State);
@@ -2066,8 +2064,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
/// Checks if the previous call to free on the given symbol failed - if free
/// failed, returns true. Also, returns the corresponding return value symbol.
-static bool didPreviousFreeFail(ProgramStateRef State, SymbolRef Sym,
- SymbolRef &RetStatusSymbol) {
+static bool didPreviousFreeFail(ProgramStateRef State,
+ SymbolRef Sym, SymbolRef &RetStatusSymbol) {
const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);
if (Ret) {
assert(*Ret && "We should not store the null return symbol");
@@ -2323,7 +2321,8 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// Check if the memory location being freed is the actual location
// allocated, or an offset.
RegionOffset Offset = R->getAsOffset();
- if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
+ if (Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
Offset.getOffset() != 0) {
const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
HandleOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
@@ -2369,8 +2368,9 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
// Normal free.
if (Hold)
- return State->set<RegionState>(
- SymBase, RefState::getRelinquished(Family, ParentExpr));
+ return State->set<RegionState>(SymBase,
+ RefState::getRelinquished(Family,
+ ParentExpr));
return State->set<RegionState>(SymBase,
RefState::getReleased(Family, ParentExpr));
@@ -2609,12 +2609,12 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext &C,
os << " allocated by " << AllocOs.str();
os << " should be deallocated by ";
- printExpectedDeallocName(os, RS->getAllocationFamily());
+ printExpectedDeallocName(os, RS->getAllocationFamily());
- if (printMemFnName(DeallocOs, C, DeallocExpr))
- os << ", not " << DeallocOs.str();
+ if (printMemFnName(DeallocOs, C, DeallocExpr))
+ os << ", not " << DeallocOs.str();
- printOwnershipTakesList(os, C, DeallocExpr);
+ printOwnershipTakesList(os, C, DeallocExpr);
}
auto R = std::make_unique<PathSensitiveBugReport>(
@@ -2651,7 +2651,8 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
assert(MR && "Only MemRegion based symbols can have offset free errors");
RegionOffset Offset = MR->getAsOffset();
- assert((Offset.isValid() && !Offset.hasSymbolicOffset() &&
+ assert((Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
Offset.getOffset() != 0) &&
"Only symbols with a valid offset can have offset free errors");
@@ -2660,8 +2661,11 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
os << "Argument to ";
if (!printMemFnName(os, C, DeallocExpr))
os << "deallocator";
- os << " is offset by " << offsetBytes << " "
- << ((abs(offsetBytes) > 1) ? "bytes" : "byte") << " from the start of ";
+ os << " is offset by "
+ << offsetBytes
+ << " "
+ << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
+ << " from the start of ";
if (AllocExpr && printMemFnName(AllocNameOs, C, AllocExpr))
os << "memory allocated by " << AllocNameOs.str();
else
@@ -2891,8 +2895,8 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
// Record the info about the reallocated symbol so that we could properly
// process failed reallocation.
- stateRealloc =
- stateRealloc->set<ReallocPairs>(ToPtr, ReallocPair(FromPtr, Kind));
+ stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
+ ReallocPair(FromPtr, Kind));
// The reallocated symbol should stay alive for as long as the new symbol.
C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
return stateRealloc;
@@ -2950,7 +2954,8 @@ MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
// Allocation node, is the last node in the current or parent context in
// which the symbol was tracked.
const LocationContext *NContext = N->getLocationContext();
- if (NContext == LeakContext || NContext->isParentOf(LeakContext))
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
AllocNode = N;
N = N->pred_empty() ? nullptr : *(N->pred_begin());
}
@@ -2986,8 +2991,9 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics();
if (AllocationStmt)
- LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
- AllocationStmt, C.getSourceManager(), AllocNode->getLocationContext());
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
+ C.getSourceManager(),
+ AllocNode->getLocationContext());
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
@@ -3009,7 +3015,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
}
void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const {
+ CheckerContext &C) const
+{
ProgramStateRef state = C.getState();
RegionStateTy OldRS = state->get<RegionState>();
RegionStateTy::Factory &F = state->get_context<RegionState>();
@@ -3027,7 +3034,8 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
if (RS == OldRS) {
// We shouldn't have touched other maps yet.
- assert(state->get<ReallocPairs>() == C.getState()->get<ReallocPairs>());
+ assert(state->get<ReallocPairs>() ==
+ C.getState()->get<ReallocPairs>());
assert(state->get<FreeReturnValue>() ==
C.getState()->get<FreeReturnValue>());
return;
@@ -3197,7 +3205,8 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
}
}
-void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
+void MallocChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
checkEscapeOnReturn(S, C);
}
@@ -3229,7 +3238,7 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
if (const MemRegion *MR = RetVal.getAsRegion())
if (isa<FieldRegion, ElementRegion>(MR))
if (const SymbolicRegion *BMR =
- dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
+ dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
Sym = BMR->getSymbol();
// Check if we are returning freed memory.
@@ -3249,13 +3258,14 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
return;
ProgramStateRef state = C.getState();
- const BlockDataRegion *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
+ const BlockDataRegion *R =
+ cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
auto ReferencedVars = R->referenced_vars();
if (ReferencedVars.empty())
return;
- SmallVector<const MemRegion *, 10> Regions;
+ SmallVector<const MemRegion*, 10> Regions;
const LocationContext *LC = C.getLocationContext();
MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
@@ -3267,7 +3277,8 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
Regions.push_back(VR);
}
- state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
+ state =
+ state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
C.addTransition(state);
}
@@ -3324,7 +3335,8 @@ void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
if (const RefState *RS = C.getState()->get<RegionState>(Sym)) {
if (RS->isAllocatedOfSizeZero())
HandleUseZeroAlloc(C, RS->getStmt()->getSourceRange(), Sym);
- } else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
+ }
+ else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
HandleUseZeroAlloc(C, S->getSourceRange(), Sym);
}
}
@@ -3341,8 +3353,9 @@ void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
// If a symbolic region is assumed to NULL (or another constant), stop tracking
// it - assuming that allocation failed on this path.
-ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SVal Cond,
- bool Assumption) const {
+ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
+ SVal Cond,
+ bool Assumption) const {
RegionStateTy RS = state->get<RegionState>();
for (SymbolRef Sym : llvm::make_first_range(RS)) {
// If the symbol is assumed to be NULL, remove it from consideration.
@@ -3367,8 +3380,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SVal Cond,
if (RS->isReleased()) {
switch (ReallocPair.Kind) {
case OAR_ToBeFreedAfterFailure:
- state = state->set<RegionState>(
- ReallocSym,
+ state = state->set<RegionState>(ReallocSym,
RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
break;
case OAR_DoNotTrackAfterFailure:
@@ -3497,8 +3509,8 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// Do not warn on pointers passed to 'setbuf' when used with std streams,
// these leaks might be intentional when setting the buffer for stdio.
// http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
- if (FName == "setbuf" || FName == "setbuffer" || FName == "setlinebuf" ||
- FName == "setvbuf") {
+ if (FName == "setbuf" || FName =="setbuffer" ||
+ FName == "setlinebuf" || FName == "setvbuf") {
if (Call->getNumArgs() >= 1) {
const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
@@ -3548,16 +3560,18 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return false;
}
-ProgramStateRef MallocChecker::checkPointerEscape(
- ProgramStateRef State, const InvalidatedSymbols &Escaped,
- const CallEvent *Call, PointerEscapeKind Kind) const {
+ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
return checkPointerEscapeAux(State, Escaped, Call, Kind,
/*IsConstPointerEscape*/ false);
}
-ProgramStateRef MallocChecker::checkConstPointerEscape(
- ProgramStateRef State, const InvalidatedSymbols &Escaped,
- const CallEvent *Call, PointerEscapeKind Kind) const {
+ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
// If a const pointer escapes, it may not be freed(), but it could be deleted.
return checkPointerEscapeAux(State, Escaped, Call, Kind,
/*IsConstPointerEscape*/ true);
@@ -3817,13 +3831,13 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {
// Is it possible to fail two reallocs WITHOUT testing in between?
assert((!FailedReallocSymbol || FailedReallocSymbol == sym) &&
- "We only support one failed realloc at a time.");
+ "We only support one failed realloc at a time.");
BR.markInteresting(sym);
FailedReallocSymbol = sym;
}
}
- // We are in a special mode if a reallocation failed later in the path.
+ // We are in a special mode if a reallocation failed later in the path.
} else if (Mode == ReallocationFailed) {
assert(FailedReallocSymbol && "No symbol to look for.");
@@ -3892,8 +3906,8 @@ namespace clang {
namespace ento {
namespace allocation_state {
-ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
- const Expr *Origin) {
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
AllocationFamily Family(AF_InnerBuffer);
return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
}
diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp
index 3e54aa900f5cc..5d5f5a633c0dd 100644
--- a/clang/test/Analysis/NewDeleteLeaks.cpp
+++ b/clang/test/Analysis/NewDeleteLeaks.cpp
@@ -11,10 +11,29 @@
// RUN: -analyzer-checker=unix \
// RUN: -analyzer-config \
// RUN: unix.DynamicMemoryModeling:AddNoOwnershipChangeNotes=true
+// RUN: %clang_analyze_cc1 -std=c++20 -analyzer-checker=cplusplus.NewDeleteLeaks -verify %s
#include "Inputs/system-header-simulator-for-malloc.h"
-#include <utility>
+// Minimal move, no headers needed, C++11+
+namespace nstd {
+
+template <class T>
+struct remove_reference { using type = T; };
+template <class T>
+struct remove_reference<T&> { using type = T; };
+template <class T>
+struct remove_reference<T&&> { using type = T; };
+
+template <class T>
+constexpr typename remove_reference<T>::type&& move(T&& t) noexcept {
+ using U = typename remove_reference<T>::type;
+ return static_cast<U&&>(t);
+}
+
+} // namespace nstd
+
+
//===----------------------------------------------------------------------===//
// Report for which we expect NoOwnershipChangeVisitor to add a new note.
@@ -222,9 +241,6 @@ void caller() {
} // namespace symbol_reaper_lifetime
-// RUN: %clang_analyze_cc1 -std=c++20 -analyzer-checker=cplusplus.NewDeleteLeaks -verify %s
-
-
// Minimal RAII class that properly deletes its pointer.
class Bar {
public:
@@ -267,7 +283,7 @@ struct FooWithConstructor {
Bar a;
Bar b;
FooWithConstructor(Bar &&original_a, Bar &&original_b)
- : a(std::move(original_a)), b(std::move(original_b)) {}
+ : a(nstd::move(original_a)), b(nstd::move(original_b)) {}
};
//===----------------------------------------------------------------------===//
More information about the cfe-commits
mailing list