Skip to content

Commit 3c6d1dd

Browse files
Xazax-hunGabor Horvath
and
Gabor Horvath
authored
Revert "[analyzer] Remove some false negatives in StackAddrEscapeChec… (#126614)
…ker (#125638)" This reverts commit 7ba3c55. Co-authored-by: Gabor Horvath <[email protected]>
1 parent 07f2154 commit 3c6d1dd

File tree

5 files changed

+81
-385
lines changed

5 files changed

+81
-385
lines changed

clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp

Lines changed: 53 additions & 129 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 EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion,
58-
const Expr *RetE) const;
57+
void EmitStackError(CheckerContext &C, const MemRegion *R,
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,38 +147,21 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
147147
return Regions;
148148
}
149149

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 {
150+
void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
151+
const MemRegion *R,
152+
const Expr *RetE) const {
166153
ExplodedNode *N = C.generateNonFatalErrorNode();
167154
if (!N)
168155
return;
169156
if (!BT_returnstack)
170157
BT_returnstack = std::make_unique<BugType>(
171158
CheckNames[CK_StackAddrEscapeChecker],
172159
"Return of address to stack-allocated memory");
173-
174160
// Generate a report for this bug.
175161
SmallString<128> buf;
176162
llvm::raw_svector_ostream os(buf);
177-
178-
// Error message formatting
179163
SourceRange range = genName(os, R, C.getASTContext());
180-
EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
181-
164+
os << " returned to caller";
182165
auto report =
183166
std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
184167
report->addRange(RetE->getSourceRange());
@@ -226,6 +209,30 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
226209
}
227210
}
228211

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+
229236
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
230237
CheckerContext &C) const {
231238
if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
@@ -240,128 +247,45 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
240247
}
241248
}
242249

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 *StackFrameContext;
253-
SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
254-
255-
public:
256-
explicit FindStackRegionsSymbolVisitor(
257-
CheckerContext &Ctxt,
258-
SmallVectorImpl<const MemRegion *> &StorageForStackRegions)
259-
: Ctxt(Ctxt), StackFrameContext(Ctxt.getStackFrame()),
260-
EscapingStackRegions(StorageForStackRegions) {}
261-
262-
bool VisitSymbol(SymbolRef sym) override { return true; }
263-
264-
bool VisitMemRegion(const MemRegion *MR) override {
265-
SaveIfEscapes(MR);
266-
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() == StackFrameContext)
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-
}
250+
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
251+
CheckerContext &C) const {
252+
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
253+
return;
290254

291-
return false;
292-
}
293-
};
255+
const Expr *RetE = RS->getRetValue();
256+
if (!RetE)
257+
return;
258+
RetE = RetE->IgnoreParens();
294259

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) {
260+
SVal V = C.getSVal(RetE);
261+
const MemRegion *R = V.getAsRegion();
262+
if (!R)
263+
return;
303264

304-
SmallVector<const MemRegion *> WillEscape;
265+
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
266+
checkReturnedBlockCaptures(*B, C);
305267

306-
const MemRegion *RetRegion = RetVal.getAsRegion();
268+
if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
269+
return;
307270

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

316279
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
317280
// so the stack address is not escaping here.
318-
bool IsCopyAndAutoreleaseBlockObj = false;
319281
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
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);
282+
if (isa<BlockDataRegion>(R) &&
283+
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
284+
return;
285+
}
330286
}
331287

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);
288+
EmitStackError(C, R, RetE);
365289
}
366290

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

clang/test/Analysis/copy-elision.cpp

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -154,26 +154,20 @@ 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
159157
ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
160-
return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
158+
return ClassWithoutDestructor(v);
161159
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
162160
object of type 'ClassWithoutDestructor' is still \
163161
referred to by the caller variable 'v' upon returning to the caller}}
164162
}
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
167163
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
168-
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
164+
return make1(v);
169165
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
170166
object of type 'ClassWithoutDestructor' is still \
171167
referred to by the caller variable 'v' upon returning to the caller}}
172168
}
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
175169
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
176-
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
170+
return make2(v);
177171
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
178172
object of type 'ClassWithoutDestructor' is still \
179173
referred to by the caller variable 'v' upon returning to the caller}}
@@ -304,26 +298,21 @@ to by the caller variable 'v' upon returning to the caller}}
304298
#endif
305299
}
306300

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
301+
309302
ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
310-
return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
303+
return ClassWithDestructor(v);
311304
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
312305
object of type 'ClassWithDestructor' is still referred \
313306
to by the caller variable 'v' upon returning to the caller}}
314307
}
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
317308
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
318-
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
309+
return make1(v);
319310
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
320311
object of type 'ClassWithDestructor' is still referred \
321312
to by the caller variable 'v' upon returning to the caller}}
322313
}
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
325314
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
326-
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
315+
return make2(v);
327316
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
328317
object of type 'ClassWithDestructor' is still referred \
329318
to by the caller variable 'v' upon returning to the caller}}

0 commit comments

Comments
 (0)