@@ -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-
236229void 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
291367static const MemSpaceRegion *getStackOrGlobalSpaceRegion (const MemRegion *R) {
0 commit comments