Skip to content

Commit 7d46945

Browse files
committed
[analyzer] Escape pointers stored into top-level parameters with destructors.
Writing stuff into an argument variable is usually equivalent to writing stuff to a local variable: it will have no effect outside of the function. There's an important exception from this rule: if the argument variable has a non-trivial destructor, the destructor would be invoked on the parent stack frame, exposing contents of the otherwise dead argument variable to the caller. If such argument is the last place where a pointer is stored before the function exits and the function is the one we've started our analysis from (i.e., we have no caller context for it), we currently diagnose a leak. This is incorrect because the destructor of the argument still has access to the pointer. The destructor may deallocate the pointer or even pass it further. Treat writes into such argument regions as "escapes" instead, suppressing spurious memory leak reports but not messing with dead symbol removal. Differential Revision: https://reviews.llvm.org/D60112 llvm-svn: 358321
1 parent 5e67abd commit 7d46945

File tree

2 files changed

+53
-34
lines changed

2 files changed

+53
-34
lines changed

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,43 +2621,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
26212621
getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this);
26222622
}
26232623

2624-
// A value escapes in three possible cases:
2624+
// A value escapes in four possible cases:
26252625
// (1) We are binding to something that is not a memory region.
2626-
// (2) We are binding to a MemrRegion that does not have stack storage.
2627-
// (3) We are binding to a MemRegion with stack storage that the store
2626+
// (2) We are binding to a MemRegion that does not have stack storage.
2627+
// (3) We are binding to a top-level parameter region with a non-trivial
2628+
// destructor. We won't see the destructor during analysis, but it's there.
2629+
// (4) We are binding to a MemRegion with stack storage that the store
26282630
// does not understand.
2629-
ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
2630-
SVal Loc,
2631-
SVal Val,
2632-
const LocationContext *LCtx) {
2633-
// Are we storing to something that causes the value to "escape"?
2634-
bool escapes = true;
2635-
2636-
// TODO: Move to StoreManager.
2637-
if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) {
2638-
escapes = !regionLoc->getRegion()->hasStackStorage();
2639-
2640-
if (!escapes) {
2641-
// To test (3), generate a new state with the binding added. If it is
2642-
// the same state, then it escapes (since the store cannot represent
2643-
// the binding).
2644-
// Do this only if we know that the store is not supposed to generate the
2645-
// same state.
2646-
SVal StoredVal = State->getSVal(regionLoc->getRegion());
2647-
if (StoredVal != Val)
2648-
escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx)));
2649-
}
2650-
}
2651-
2652-
// If our store can represent the binding and we aren't storing to something
2653-
// that doesn't have local storage then just return and have the simulation
2654-
// state continue as is.
2655-
if (!escapes)
2656-
return State;
2631+
ProgramStateRef
2632+
ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc,
2633+
SVal Val, const LocationContext *LCtx) {
2634+
2635+
// Cases (1) and (2).
2636+
const MemRegion *MR = Loc.getAsRegion();
2637+
if (!MR || !MR->hasStackStorage())
2638+
return escapeValue(State, Val, PSK_EscapeOnBind);
2639+
2640+
// Case (3).
2641+
if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
2642+
if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
2643+
if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl())
2644+
if (!RD->hasTrivialDestructor())
2645+
return escapeValue(State, Val, PSK_EscapeOnBind);
2646+
2647+
// Case (4): in order to test that, generate a new state with the binding
2648+
// added. If it is the same state, then it escapes (since the store cannot
2649+
// represent the binding).
2650+
// Do this only if we know that the store is not supposed to generate the
2651+
// same state.
2652+
SVal StoredVal = State->getSVal(MR);
2653+
if (StoredVal != Val)
2654+
if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx)))
2655+
return escapeValue(State, Val, PSK_EscapeOnBind);
26572656

2658-
// Otherwise, find all symbols referenced by 'val' that we are tracking
2659-
// and stop tracking them.
2660-
State = escapeValue(State, Val, PSK_EscapeOnBind);
26612657
return State;
26622658
}
26632659

clang/test/Analysis/malloc.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,26 @@ char* test_cxa_demangle(const char* sym) {
141141
}
142142
return funcname; // no-warning
143143
}
144+
145+
namespace argument_leak {
146+
class A {
147+
char *name;
148+
149+
public:
150+
char *getName() {
151+
if (!name) {
152+
name = static_cast<char *>(malloc(10));
153+
}
154+
return name;
155+
}
156+
~A() {
157+
if (name) {
158+
delete[] name;
159+
}
160+
}
161+
};
162+
163+
void test(A a) {
164+
(void)a.getName();
165+
}
166+
} // namespace argument_leak

0 commit comments

Comments
 (0)