Skip to content

Commit 560149b

Browse files
authored
[analyzer] Reapply recent stack addr escape checker changes + buildbot fix (#126620)
Reapplying changes from #125638 after buildbot failures. Buildbot failures fixed in 029e7e9, latest commit on this PR. It was a problem with a declared class member with same name as its type. Sorry!
1 parent e258bca commit 560149b

File tree

5 files changed

+385
-81
lines changed

5 files changed

+385
-81
lines changed

clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp

Lines changed: 129 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class StackAddrEscapeChecker
5454
CheckerContext &C) const;
5555
void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
5656
CheckerContext &C) const;
57-
void EmitStackError(CheckerContext &C, const MemRegion *R,
58-
const Expr *RetE) const;
57+
void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion,
58+
const Expr *RetE) const;
5959
bool isSemaphoreCaptured(const BlockDecl &B) const;
6060
static SourceRange genName(raw_ostream &os, const MemRegion *R,
6161
ASTContext &Ctx);
@@ -147,21 +147,38 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
147147
return Regions;
148148
}
149149

150-
void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
151-
const MemRegion *R,
152-
const Expr *RetE) const {
150+
static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal,
151+
const MemRegion *LeakedRegion) {
152+
if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) {
153+
if (isa<BlockDataRegion>(ReturnedRegion)) {
154+
OS << " is captured by a returned block";
155+
return;
156+
}
157+
}
158+
159+
// Generic message
160+
OS << " returned to caller";
161+
}
162+
163+
void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
164+
const MemRegion *R,
165+
const Expr *RetE) const {
153166
ExplodedNode *N = C.generateNonFatalErrorNode();
154167
if (!N)
155168
return;
156169
if (!BT_returnstack)
157170
BT_returnstack = std::make_unique<BugType>(
158171
CheckNames[CK_StackAddrEscapeChecker],
159172
"Return of address to stack-allocated memory");
173+
160174
// Generate a report for this bug.
161175
SmallString<128> buf;
162176
llvm::raw_svector_ostream os(buf);
177+
178+
// Error message formatting
163179
SourceRange range = genName(os, R, C.getASTContext());
164-
os << " returned to caller";
180+
EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
181+
165182
auto report =
166183
std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
167184
report->addRange(RetE->getSourceRange());
@@ -209,30 +226,6 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
209226
}
210227
}
211228

212-
void StackAddrEscapeChecker::checkReturnedBlockCaptures(
213-
const BlockDataRegion &B, CheckerContext &C) const {
214-
for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
215-
if (isNotInCurrentFrame(Region, C))
216-
continue;
217-
ExplodedNode *N = C.generateNonFatalErrorNode();
218-
if (!N)
219-
continue;
220-
if (!BT_capturedstackret)
221-
BT_capturedstackret = std::make_unique<BugType>(
222-
CheckNames[CK_StackAddrEscapeChecker],
223-
"Address of stack-allocated memory is captured");
224-
SmallString<128> Buf;
225-
llvm::raw_svector_ostream Out(Buf);
226-
SourceRange Range = genName(Out, Region, C.getASTContext());
227-
Out << " is captured by a returned block";
228-
auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
229-
Out.str(), N);
230-
if (Range.isValid())
231-
Report->addRange(Range);
232-
C.emitReport(std::move(Report));
233-
}
234-
}
235-
236229
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
237230
CheckerContext &C) const {
238231
if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
@@ -247,45 +240,128 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
247240
}
248241
}
249242

250-
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
251-
CheckerContext &C) const {
252-
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
253-
return;
243+
/// A visitor made for use with a ScanReachableSymbols scanner, used
244+
/// for finding stack regions within an SVal that live on the current
245+
/// stack frame of the given checker context. This visitor excludes
246+
/// NonParamVarRegion that data is bound to in a BlockDataRegion's
247+
/// bindings, since these are likely uninteresting, e.g., in case a
248+
/// temporary is constructed on the stack, but it captures values
249+
/// that would leak.
250+
class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
251+
CheckerContext &Ctxt;
252+
const StackFrameContext *PoppedStackFrame;
253+
SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
254254

255-
const Expr *RetE = RS->getRetValue();
256-
if (!RetE)
257-
return;
258-
RetE = RetE->IgnoreParens();
255+
public:
256+
explicit FindStackRegionsSymbolVisitor(
257+
CheckerContext &Ctxt,
258+
SmallVectorImpl<const MemRegion *> &StorageForStackRegions)
259+
: Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),
260+
EscapingStackRegions(StorageForStackRegions) {}
259261

260-
SVal V = C.getSVal(RetE);
261-
const MemRegion *R = V.getAsRegion();
262-
if (!R)
263-
return;
262+
bool VisitSymbol(SymbolRef sym) override { return true; }
264263

265-
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
266-
checkReturnedBlockCaptures(*B, C);
264+
bool VisitMemRegion(const MemRegion *MR) override {
265+
SaveIfEscapes(MR);
267266

268-
if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
269-
return;
267+
if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>())
268+
return VisitBlockDataRegionCaptures(BDR);
269+
270+
return true;
271+
}
272+
273+
private:
274+
void SaveIfEscapes(const MemRegion *MR) {
275+
const StackSpaceRegion *SSR =
276+
MR->getMemorySpace()->getAs<StackSpaceRegion>();
277+
if (SSR && SSR->getStackFrame() == PoppedStackFrame)
278+
EscapingStackRegions.push_back(MR);
279+
}
280+
281+
bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) {
282+
for (auto Var : BDR->referenced_vars()) {
283+
SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());
284+
const MemRegion *Region = Val.getAsRegion();
285+
if (Region) {
286+
SaveIfEscapes(Region);
287+
VisitMemRegion(Region);
288+
}
289+
}
290+
291+
return false;
292+
}
293+
};
294+
295+
/// Given some memory regions that are flagged by FindStackRegionsSymbolVisitor,
296+
/// this function filters out memory regions that are being returned that are
297+
/// likely not true leaks:
298+
/// 1. If returning a block data region that has stack memory space
299+
/// 2. If returning a constructed object that has stack memory space
300+
static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(
301+
const SmallVectorImpl<const MemRegion *> &MaybeEscaped, CheckerContext &C,
302+
const Expr *RetE, SVal &RetVal) {
303+
304+
SmallVector<const MemRegion *> WillEscape;
305+
306+
const MemRegion *RetRegion = RetVal.getAsRegion();
270307

271308
// Returning a record by value is fine. (In this case, the returned
272309
// expression will be a copy-constructor, possibly wrapped in an
273310
// ExprWithCleanups node.)
274311
if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
275312
RetE = Cleanup->getSubExpr();
276-
if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
277-
return;
313+
bool IsConstructExpr =
314+
isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType();
278315

279316
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
280317
// so the stack address is not escaping here.
318+
bool IsCopyAndAutoreleaseBlockObj = false;
281319
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
282-
if (isa<BlockDataRegion>(R) &&
283-
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
284-
return;
285-
}
320+
IsCopyAndAutoreleaseBlockObj =
321+
isa_and_nonnull<BlockDataRegion>(RetRegion) &&
322+
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;
323+
}
324+
325+
for (const MemRegion *MR : MaybeEscaped) {
326+
if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))
327+
continue;
328+
329+
WillEscape.push_back(MR);
286330
}
287331

288-
EmitStackError(C, R, RetE);
332+
return WillEscape;
333+
}
334+
335+
/// For use in finding regions that live on the checker context's current
336+
/// stack frame, deep in the SVal representing the return value.
337+
static SmallVector<const MemRegion *>
338+
FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) {
339+
SmallVector<const MemRegion *> FoundStackRegions;
340+
341+
FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions);
342+
ScanReachableSymbols Scanner(C.getState(), Finder);
343+
Scanner.scan(RetVal);
344+
345+
return FilterReturnExpressionLeaks(FoundStackRegions, C, RetE, RetVal);
346+
}
347+
348+
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
349+
CheckerContext &C) const {
350+
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
351+
return;
352+
353+
const Expr *RetE = RS->getRetValue();
354+
if (!RetE)
355+
return;
356+
RetE = RetE->IgnoreParens();
357+
358+
SVal V = C.getSVal(RetE);
359+
360+
SmallVector<const MemRegion *> EscapedStackRegions =
361+
FindEscapingStackRegions(C, RetE, V);
362+
363+
for (const MemRegion *ER : EscapedStackRegions)
364+
EmitReturnLeakError(C, ER, RetE);
289365
}
290366

291367
static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {

clang/test/Analysis/copy-elision.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,26 @@ class ClassWithoutDestructor {
154154
void push() { v.push(this); }
155155
};
156156

157+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
158+
// are returning an object which holds v which holds the address of the temporary
157159
ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
158-
return ClassWithoutDestructor(v);
160+
return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
159161
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
160162
object of type 'ClassWithoutDestructor' is still \
161163
referred to by the caller variable 'v' upon returning to the caller}}
162164
}
165+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
166+
// are returning an object which holds v which holds the address of the temporary
163167
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
164-
return make1(v);
168+
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
165169
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
166170
object of type 'ClassWithoutDestructor' is still \
167171
referred to by the caller variable 'v' upon returning to the caller}}
168172
}
173+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
174+
// are returning an object which holds v which holds the address of the temporary
169175
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
170-
return make2(v);
176+
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
171177
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
172178
object of type 'ClassWithoutDestructor' is still \
173179
referred to by the caller variable 'v' upon returning to the caller}}
@@ -298,21 +304,26 @@ to by the caller variable 'v' upon returning to the caller}}
298304
#endif
299305
}
300306

301-
307+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
308+
// are returning an object which holds v which holds the address of the temporary
302309
ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
303-
return ClassWithDestructor(v);
310+
return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
304311
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
305312
object of type 'ClassWithDestructor' is still referred \
306313
to by the caller variable 'v' upon returning to the caller}}
307314
}
315+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
316+
// are returning an object which holds v which holds the address of the temporary
308317
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
309-
return make1(v);
318+
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
310319
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
311320
object of type 'ClassWithDestructor' is still referred \
312321
to by the caller variable 'v' upon returning to the caller}}
313322
}
323+
// Two warnings on no-elide: arg v holds the address of the temporary, and we
324+
// are returning an object which holds v which holds the address of the temporary
314325
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
315-
return make2(v);
326+
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
316327
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
317328
object of type 'ClassWithDestructor' is still referred \
318329
to by the caller variable 'v' upon returning to the caller}}

0 commit comments

Comments
 (0)