@@ -54,8 +54,8 @@ class StackAddrEscapeChecker
54
54
CheckerContext &C) const ;
55
55
void checkAsyncExecutedBlockCaptures (const BlockDataRegion &B,
56
56
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 ;
59
59
bool isSemaphoreCaptured (const BlockDecl &B) const ;
60
60
static SourceRange genName (raw_ostream &os, const MemRegion *R,
61
61
ASTContext &Ctx);
@@ -147,21 +147,38 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
147
147
return Regions;
148
148
}
149
149
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 {
153
166
ExplodedNode *N = C.generateNonFatalErrorNode ();
154
167
if (!N)
155
168
return ;
156
169
if (!BT_returnstack)
157
170
BT_returnstack = std::make_unique<BugType>(
158
171
CheckNames[CK_StackAddrEscapeChecker],
159
172
" Return of address to stack-allocated memory" );
173
+
160
174
// Generate a report for this bug.
161
175
SmallString<128 > buf;
162
176
llvm::raw_svector_ostream os (buf);
177
+
178
+ // Error message formatting
163
179
SourceRange range = genName (os, R, C.getASTContext ());
164
- os << " returned to caller" ;
180
+ EmitReturnedAsPartOfError (os, C.getSVal (RetE), R);
181
+
165
182
auto report =
166
183
std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str (), N);
167
184
report->addRange (RetE->getSourceRange ());
@@ -209,30 +226,6 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
209
226
}
210
227
}
211
228
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
-
236
229
void StackAddrEscapeChecker::checkPreCall (const CallEvent &Call,
237
230
CheckerContext &C) const {
238
231
if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
@@ -247,45 +240,128 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
247
240
}
248
241
}
249
242
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;
254
254
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) {}
259
261
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 ; }
264
263
265
- if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
266
- checkReturnedBlockCaptures (*B, C );
264
+ bool VisitMemRegion (const MemRegion *MR) override {
265
+ SaveIfEscapes (MR );
267
266
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 ();
270
307
271
308
// Returning a record by value is fine. (In this case, the returned
272
309
// expression will be a copy-constructor, possibly wrapped in an
273
310
// ExprWithCleanups node.)
274
311
if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
275
312
RetE = Cleanup->getSubExpr ();
276
- if (isa<CXXConstructExpr>(RetE) && RetE-> getType ()-> isRecordType ())
277
- return ;
313
+ bool IsConstructExpr =
314
+ isa<CXXConstructExpr>(RetE) && RetE-> getType ()-> isRecordType () ;
278
315
279
316
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
280
317
// so the stack address is not escaping here.
318
+ bool IsCopyAndAutoreleaseBlockObj = false ;
281
319
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);
286
330
}
287
331
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);
289
365
}
290
366
291
367
static const MemSpaceRegion *getStackOrGlobalSpaceRegion (const MemRegion *R) {
0 commit comments