[analyzer] Reapply recent stack addr escape checker changes + buildbot fix (#126620)
Reapplying changes from https://github.com/llvm/llvm-project/pull/125638 after buildbot failures. Buildbot failures fixed in 029e7e98dc9956086adc6c1dfb0c655a273fbee6, latest commit on this PR. It was a problem with a declared class member with same name as its type. Sorry!
This commit is contained in:
parent
e258bca950
commit
560149b5e3
@ -54,8 +54,8 @@ private:
|
|||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
|
void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
|
||||||
CheckerContext &C) const;
|
CheckerContext &C) const;
|
||||||
void EmitStackError(CheckerContext &C, const MemRegion *R,
|
void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion,
|
||||||
const Expr *RetE) const;
|
const Expr *RetE) const;
|
||||||
bool isSemaphoreCaptured(const BlockDecl &B) const;
|
bool isSemaphoreCaptured(const BlockDecl &B) const;
|
||||||
static SourceRange genName(raw_ostream &os, const MemRegion *R,
|
static SourceRange genName(raw_ostream &os, const MemRegion *R,
|
||||||
ASTContext &Ctx);
|
ASTContext &Ctx);
|
||||||
@ -147,9 +147,22 @@ StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
|
|||||||
return Regions;
|
return Regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
|
static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal,
|
||||||
const MemRegion *R,
|
const MemRegion *LeakedRegion) {
|
||||||
const Expr *RetE) const {
|
if (const MemRegion *ReturnedRegion = ReturnedVal.getAsRegion()) {
|
||||||
|
if (isa<BlockDataRegion>(ReturnedRegion)) {
|
||||||
|
OS << " is captured by a returned block";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic message
|
||||||
|
OS << " returned to caller";
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,
|
||||||
|
const MemRegion *R,
|
||||||
|
const Expr *RetE) const {
|
||||||
ExplodedNode *N = C.generateNonFatalErrorNode();
|
ExplodedNode *N = C.generateNonFatalErrorNode();
|
||||||
if (!N)
|
if (!N)
|
||||||
return;
|
return;
|
||||||
@ -157,11 +170,15 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
|
|||||||
BT_returnstack = std::make_unique<BugType>(
|
BT_returnstack = std::make_unique<BugType>(
|
||||||
CheckNames[CK_StackAddrEscapeChecker],
|
CheckNames[CK_StackAddrEscapeChecker],
|
||||||
"Return of address to stack-allocated memory");
|
"Return of address to stack-allocated memory");
|
||||||
|
|
||||||
// Generate a report for this bug.
|
// Generate a report for this bug.
|
||||||
SmallString<128> buf;
|
SmallString<128> buf;
|
||||||
llvm::raw_svector_ostream os(buf);
|
llvm::raw_svector_ostream os(buf);
|
||||||
|
|
||||||
|
// Error message formatting
|
||||||
SourceRange range = genName(os, R, C.getASTContext());
|
SourceRange range = genName(os, R, C.getASTContext());
|
||||||
os << " returned to caller";
|
EmitReturnedAsPartOfError(os, C.getSVal(RetE), R);
|
||||||
|
|
||||||
auto report =
|
auto report =
|
||||||
std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
|
std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
|
||||||
report->addRange(RetE->getSourceRange());
|
report->addRange(RetE->getSourceRange());
|
||||||
@ -209,30 +226,6 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackAddrEscapeChecker::checkReturnedBlockCaptures(
|
|
||||||
const BlockDataRegion &B, CheckerContext &C) const {
|
|
||||||
for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
|
|
||||||
if (isNotInCurrentFrame(Region, C))
|
|
||||||
continue;
|
|
||||||
ExplodedNode *N = C.generateNonFatalErrorNode();
|
|
||||||
if (!N)
|
|
||||||
continue;
|
|
||||||
if (!BT_capturedstackret)
|
|
||||||
BT_capturedstackret = std::make_unique<BugType>(
|
|
||||||
CheckNames[CK_StackAddrEscapeChecker],
|
|
||||||
"Address of stack-allocated memory is captured");
|
|
||||||
SmallString<128> Buf;
|
|
||||||
llvm::raw_svector_ostream Out(Buf);
|
|
||||||
SourceRange Range = genName(Out, Region, C.getASTContext());
|
|
||||||
Out << " is captured by a returned block";
|
|
||||||
auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
|
|
||||||
Out.str(), N);
|
|
||||||
if (Range.isValid())
|
|
||||||
Report->addRange(Range);
|
|
||||||
C.emitReport(std::move(Report));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
|
void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
|
if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
|
||||||
@ -247,6 +240,111 @@ void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A visitor made for use with a ScanReachableSymbols scanner, used
|
||||||
|
/// for finding stack regions within an SVal that live on the current
|
||||||
|
/// stack frame of the given checker context. This visitor excludes
|
||||||
|
/// NonParamVarRegion that data is bound to in a BlockDataRegion's
|
||||||
|
/// bindings, since these are likely uninteresting, e.g., in case a
|
||||||
|
/// temporary is constructed on the stack, but it captures values
|
||||||
|
/// that would leak.
|
||||||
|
class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
|
||||||
|
CheckerContext &Ctxt;
|
||||||
|
const StackFrameContext *PoppedStackFrame;
|
||||||
|
SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FindStackRegionsSymbolVisitor(
|
||||||
|
CheckerContext &Ctxt,
|
||||||
|
SmallVectorImpl<const MemRegion *> &StorageForStackRegions)
|
||||||
|
: Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),
|
||||||
|
EscapingStackRegions(StorageForStackRegions) {}
|
||||||
|
|
||||||
|
bool VisitSymbol(SymbolRef sym) override { return true; }
|
||||||
|
|
||||||
|
bool VisitMemRegion(const MemRegion *MR) override {
|
||||||
|
SaveIfEscapes(MR);
|
||||||
|
|
||||||
|
if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>())
|
||||||
|
return VisitBlockDataRegionCaptures(BDR);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SaveIfEscapes(const MemRegion *MR) {
|
||||||
|
const StackSpaceRegion *SSR =
|
||||||
|
MR->getMemorySpace()->getAs<StackSpaceRegion>();
|
||||||
|
if (SSR && SSR->getStackFrame() == PoppedStackFrame)
|
||||||
|
EscapingStackRegions.push_back(MR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) {
|
||||||
|
for (auto Var : BDR->referenced_vars()) {
|
||||||
|
SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());
|
||||||
|
const MemRegion *Region = Val.getAsRegion();
|
||||||
|
if (Region) {
|
||||||
|
SaveIfEscapes(Region);
|
||||||
|
VisitMemRegion(Region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Given some memory regions that are flagged by FindStackRegionsSymbolVisitor,
|
||||||
|
/// this function filters out memory regions that are being returned that are
|
||||||
|
/// likely not true leaks:
|
||||||
|
/// 1. If returning a block data region that has stack memory space
|
||||||
|
/// 2. If returning a constructed object that has stack memory space
|
||||||
|
static SmallVector<const MemRegion *> FilterReturnExpressionLeaks(
|
||||||
|
const SmallVectorImpl<const MemRegion *> &MaybeEscaped, CheckerContext &C,
|
||||||
|
const Expr *RetE, SVal &RetVal) {
|
||||||
|
|
||||||
|
SmallVector<const MemRegion *> WillEscape;
|
||||||
|
|
||||||
|
const MemRegion *RetRegion = RetVal.getAsRegion();
|
||||||
|
|
||||||
|
// Returning a record by value is fine. (In this case, the returned
|
||||||
|
// expression will be a copy-constructor, possibly wrapped in an
|
||||||
|
// ExprWithCleanups node.)
|
||||||
|
if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
|
||||||
|
RetE = Cleanup->getSubExpr();
|
||||||
|
bool IsConstructExpr =
|
||||||
|
isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType();
|
||||||
|
|
||||||
|
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
|
||||||
|
// so the stack address is not escaping here.
|
||||||
|
bool IsCopyAndAutoreleaseBlockObj = false;
|
||||||
|
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
|
||||||
|
IsCopyAndAutoreleaseBlockObj =
|
||||||
|
isa_and_nonnull<BlockDataRegion>(RetRegion) &&
|
||||||
|
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MemRegion *MR : MaybeEscaped) {
|
||||||
|
if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WillEscape.push_back(MR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WillEscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For use in finding regions that live on the checker context's current
|
||||||
|
/// stack frame, deep in the SVal representing the return value.
|
||||||
|
static SmallVector<const MemRegion *>
|
||||||
|
FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal) {
|
||||||
|
SmallVector<const MemRegion *> FoundStackRegions;
|
||||||
|
|
||||||
|
FindStackRegionsSymbolVisitor Finder(C, FoundStackRegions);
|
||||||
|
ScanReachableSymbols Scanner(C.getState(), Finder);
|
||||||
|
Scanner.scan(RetVal);
|
||||||
|
|
||||||
|
return FilterReturnExpressionLeaks(FoundStackRegions, C, RetE, RetVal);
|
||||||
|
}
|
||||||
|
|
||||||
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
||||||
CheckerContext &C) const {
|
CheckerContext &C) const {
|
||||||
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
|
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
|
||||||
@ -258,34 +356,12 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
|
|||||||
RetE = RetE->IgnoreParens();
|
RetE = RetE->IgnoreParens();
|
||||||
|
|
||||||
SVal V = C.getSVal(RetE);
|
SVal V = C.getSVal(RetE);
|
||||||
const MemRegion *R = V.getAsRegion();
|
|
||||||
if (!R)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
|
SmallVector<const MemRegion *> EscapedStackRegions =
|
||||||
checkReturnedBlockCaptures(*B, C);
|
FindEscapingStackRegions(C, RetE, V);
|
||||||
|
|
||||||
if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
|
for (const MemRegion *ER : EscapedStackRegions)
|
||||||
return;
|
EmitReturnLeakError(C, ER, RetE);
|
||||||
|
|
||||||
// Returning a record by value is fine. (In this case, the returned
|
|
||||||
// expression will be a copy-constructor, possibly wrapped in an
|
|
||||||
// ExprWithCleanups node.)
|
|
||||||
if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
|
|
||||||
RetE = Cleanup->getSubExpr();
|
|
||||||
if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
|
|
||||||
// so the stack address is not escaping here.
|
|
||||||
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
|
|
||||||
if (isa<BlockDataRegion>(R) &&
|
|
||||||
ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitStackError(C, R, RetE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {
|
static const MemSpaceRegion *getStackOrGlobalSpaceRegion(const MemRegion *R) {
|
||||||
|
@ -154,20 +154,26 @@ public:
|
|||||||
void push() { v.push(this); }
|
void push() { v.push(this); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
|
ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
|
||||||
return ClassWithoutDestructor(v);
|
return ClassWithoutDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithoutDestructor' is still \
|
object of type 'ClassWithoutDestructor' is still \
|
||||||
referred to by the caller variable 'v' upon returning to the caller}}
|
referred to by the caller variable 'v' upon returning to the caller}}
|
||||||
}
|
}
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
|
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
|
||||||
return make1(v);
|
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithoutDestructor' is still \
|
object of type 'ClassWithoutDestructor' is still \
|
||||||
referred to by the caller variable 'v' upon returning to the caller}}
|
referred to by the caller variable 'v' upon returning to the caller}}
|
||||||
}
|
}
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
|
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
|
||||||
return make2(v);
|
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithoutDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithoutDestructor' is still \
|
object of type 'ClassWithoutDestructor' is still \
|
||||||
referred to by the caller variable 'v' upon returning to the caller}}
|
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}}
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
|
ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
|
||||||
return ClassWithDestructor(v);
|
return ClassWithDestructor(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithDestructor' is still referred \
|
object of type 'ClassWithDestructor' is still referred \
|
||||||
to by the caller variable 'v' upon returning to the caller}}
|
to by the caller variable 'v' upon returning to the caller}}
|
||||||
}
|
}
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
|
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
|
||||||
return make1(v);
|
return make1(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithDestructor' is still referred \
|
object of type 'ClassWithDestructor' is still referred \
|
||||||
to by the caller variable 'v' upon returning to the caller}}
|
to by the caller variable 'v' upon returning to the caller}}
|
||||||
}
|
}
|
||||||
|
// Two warnings on no-elide: arg v holds the address of the temporary, and we
|
||||||
|
// are returning an object which holds v which holds the address of the temporary
|
||||||
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
|
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
|
||||||
return make2(v);
|
return make2(v); // no-elide-warning{{Address of stack memory associated with temporary object of type 'ClassWithDestructor' returned to caller}}
|
||||||
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
|
||||||
object of type 'ClassWithDestructor' is still referred \
|
object of type 'ClassWithDestructor' is still referred \
|
||||||
to by the caller variable 'v' upon returning to the caller}}
|
to by the caller variable 'v' upon returning to the caller}}
|
||||||
|
@ -90,6 +90,18 @@ int *mf() {
|
|||||||
return &x; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{address of stack memory associated with local variable 's1' returned}}
|
return &x; // expected-warning{{Address of stack memory associated with local variable 's1' returned}} expected-warning {{address of stack memory associated with local variable 's1' returned}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int *return_assign_expr_leak() {
|
||||||
|
int x = 1;
|
||||||
|
int *y;
|
||||||
|
return y = &x; // expected-warning{{Address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional diagnostic from -Wreturn-stack-address in the simple case?
|
||||||
|
int *return_comma_separated_expressions_leak() {
|
||||||
|
int x = 1;
|
||||||
|
return (x=14), &x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning{{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
void *lf() {
|
void *lf() {
|
||||||
label:
|
label:
|
||||||
void *const &x = &&label; // expected-note {{binding reference variable 'x' here}}
|
void *const &x = &&label; // expected-note {{binding reference variable 'x' here}}
|
||||||
@ -152,6 +164,18 @@ namespace rdar13296133 {
|
|||||||
}
|
}
|
||||||
} // namespace rdar13296133
|
} // namespace rdar13296133
|
||||||
|
|
||||||
|
void* ret_cpp_static_cast(short x) {
|
||||||
|
return static_cast<void*>(&x); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_cpp_reinterpret_cast(double x) {
|
||||||
|
return reinterpret_cast<int*>(&x); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack me}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_cpp_const_cast(const int x) {
|
||||||
|
return const_cast<int*>(&x); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory}}
|
||||||
|
}
|
||||||
|
|
||||||
void write_stack_address_to(char **q) {
|
void write_stack_address_to(char **q) {
|
||||||
char local;
|
char local;
|
||||||
*q = &local;
|
*q = &local;
|
||||||
@ -178,6 +202,11 @@ void test_copy_elision() {
|
|||||||
C c1 = make1();
|
C c1 = make1();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
C&& return_bind_rvalue_reference_to_temporary() {
|
||||||
|
return C(); // expected-warning{{Address of stack memory associated with temporary object of type 'C' returned to caller}}
|
||||||
|
// expected-warning@-1{{returning reference to local temporary object}} -Wreturn-stack-address
|
||||||
|
}
|
||||||
|
|
||||||
namespace leaking_via_direct_pointer {
|
namespace leaking_via_direct_pointer {
|
||||||
void* returned_direct_pointer_top() {
|
void* returned_direct_pointer_top() {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
@ -251,7 +280,7 @@ void* lambda_to_context_direct_pointer_uncalled() {
|
|||||||
int local = 42;
|
int local = 42;
|
||||||
p = &local; // no-warning: analyzed only as top-level, ignored explicitly by the checker
|
p = &local; // no-warning: analyzed only as top-level, ignored explicitly by the checker
|
||||||
};
|
};
|
||||||
return new MyFunction(&lambda);
|
return new MyFunction(&lambda); // expected-warning{{Address of stack memory associated with local variable 'lambda' returned to caller}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lambda_to_context_direct_pointer_lifetime_extended() {
|
void lambda_to_context_direct_pointer_lifetime_extended() {
|
||||||
@ -340,6 +369,13 @@ void param_ptr_to_ptr_to_ptr_callee(void*** ppp) {
|
|||||||
**ppp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
|
**ppp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ***param_ptr_to_ptr_to_ptr_return(void ***ppp) {
|
||||||
|
int local = 0;
|
||||||
|
**ppp = &local;
|
||||||
|
return ppp;
|
||||||
|
// expected-warning@-1 {{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ppp' upon returning to the caller. This will be a dangling reference}}
|
||||||
|
}
|
||||||
|
|
||||||
void param_ptr_to_ptr_to_ptr_caller(void** pp) {
|
void param_ptr_to_ptr_to_ptr_caller(void** pp) {
|
||||||
param_ptr_to_ptr_to_ptr_callee(&pp);
|
param_ptr_to_ptr_to_ptr_callee(&pp);
|
||||||
}
|
}
|
||||||
@ -410,16 +446,16 @@ void** returned_arr_of_ptr_top() {
|
|||||||
int* p = &local;
|
int* p = &local;
|
||||||
void** arr = new void*[2];
|
void** arr = new void*[2];
|
||||||
arr[1] = p;
|
arr[1] = p;
|
||||||
return arr;
|
return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void** returned_arr_of_ptr_callee() {
|
void** returned_arr_of_ptr_callee() {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
int* p = &local;
|
int* p = &local;
|
||||||
void** arr = new void*[2];
|
void** arr = new void*[2];
|
||||||
arr[1] = p;
|
arr[1] = p;
|
||||||
return arr;
|
return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void returned_arr_of_ptr_caller() {
|
void returned_arr_of_ptr_caller() {
|
||||||
void** arr = returned_arr_of_ptr_callee();
|
void** arr = returned_arr_of_ptr_callee();
|
||||||
@ -466,16 +502,16 @@ void** returned_arr_of_ptr_top(int idx) {
|
|||||||
int* p = &local;
|
int* p = &local;
|
||||||
void** arr = new void*[2];
|
void** arr = new void*[2];
|
||||||
arr[idx] = p;
|
arr[idx] = p;
|
||||||
return arr;
|
return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void** returned_arr_of_ptr_callee(int idx) {
|
void** returned_arr_of_ptr_callee(int idx) {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
int* p = &local;
|
int* p = &local;
|
||||||
void** arr = new void*[2];
|
void** arr = new void*[2];
|
||||||
arr[idx] = p;
|
arr[idx] = p;
|
||||||
return arr;
|
return arr; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void returned_arr_of_ptr_caller(int idx) {
|
void returned_arr_of_ptr_caller(int idx) {
|
||||||
void** arr = returned_arr_of_ptr_callee(idx);
|
void** arr = returned_arr_of_ptr_callee(idx);
|
||||||
@ -525,14 +561,25 @@ S returned_struct_with_ptr_top() {
|
|||||||
int local = 42;
|
int local = 42;
|
||||||
S s;
|
S s;
|
||||||
s.p = &local;
|
s.p = &local;
|
||||||
return s;
|
return s; // expected-warning{{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative, requires traversing returned LazyCompoundVals
|
}
|
||||||
|
|
||||||
S returned_struct_with_ptr_callee() {
|
S returned_struct_with_ptr_callee() {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
S s;
|
S s;
|
||||||
s.p = &local;
|
s.p = &local;
|
||||||
return s; // expected-warning{{'local' is still referred to by the caller variable 's'}}
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}} expected-warning{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 's' upon returning to the caller. This will be a dangling reference}}
|
||||||
|
}
|
||||||
|
|
||||||
|
S leak_through_field_of_returned_object() {
|
||||||
|
int local = 14;
|
||||||
|
S s{&local};
|
||||||
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
|
}
|
||||||
|
|
||||||
|
S leak_through_compound_literal() {
|
||||||
|
int local = 0;
|
||||||
|
return (S) { &local }; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void returned_struct_with_ptr_caller() {
|
void returned_struct_with_ptr_caller() {
|
||||||
@ -555,6 +602,30 @@ void static_struct_with_ptr() {
|
|||||||
}
|
}
|
||||||
} // namespace leaking_via_struct_with_ptr
|
} // namespace leaking_via_struct_with_ptr
|
||||||
|
|
||||||
|
namespace leaking_via_nested_structs_with_ptr {
|
||||||
|
struct Inner {
|
||||||
|
int *ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Outer {
|
||||||
|
Inner I;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Deriving : public Outer {};
|
||||||
|
|
||||||
|
Outer leaks_through_nested_objects() {
|
||||||
|
int local = 0;
|
||||||
|
Outer O{&local};
|
||||||
|
return O; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
|
}
|
||||||
|
|
||||||
|
Deriving leaks_through_base_objects() {
|
||||||
|
int local = 0;
|
||||||
|
Deriving D{&local};
|
||||||
|
return D; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
|
}
|
||||||
|
} // namespace leaking_via_nested_structs_with_ptr
|
||||||
|
|
||||||
namespace leaking_via_ref_to_struct_with_ptr {
|
namespace leaking_via_ref_to_struct_with_ptr {
|
||||||
struct S {
|
struct S {
|
||||||
int* p;
|
int* p;
|
||||||
@ -613,15 +684,15 @@ S* returned_ptr_to_struct_with_ptr_top() {
|
|||||||
int local = 42;
|
int local = 42;
|
||||||
S* s = new S;
|
S* s = new S;
|
||||||
s->p = &local;
|
s->p = &local;
|
||||||
return s;
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
S* returned_ptr_to_struct_with_ptr_callee() {
|
S* returned_ptr_to_struct_with_ptr_callee() {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
S* s = new S;
|
S* s = new S;
|
||||||
s->p = &local;
|
s->p = &local;
|
||||||
return s;
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void returned_ptr_to_struct_with_ptr_caller() {
|
void returned_ptr_to_struct_with_ptr_caller() {
|
||||||
S* s = returned_ptr_to_struct_with_ptr_callee();
|
S* s = returned_ptr_to_struct_with_ptr_callee();
|
||||||
@ -676,15 +747,15 @@ S* returned_ptr_to_struct_with_ptr_top() {
|
|||||||
int local = 42;
|
int local = 42;
|
||||||
S* s = new S[2];
|
S* s = new S[2];
|
||||||
s[1].p = &local;
|
s[1].p = &local;
|
||||||
return s;
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
S* returned_ptr_to_struct_with_ptr_callee() {
|
S* returned_ptr_to_struct_with_ptr_callee() {
|
||||||
int local = 42;
|
int local = 42;
|
||||||
S* s = new S[2];
|
S* s = new S[2];
|
||||||
s[1].p = &local;
|
s[1].p = &local;
|
||||||
return s;
|
return s; // expected-warning {{Address of stack memory associated with local variable 'local' returned to caller}}
|
||||||
} // no-warning False Negative
|
}
|
||||||
|
|
||||||
void returned_ptr_to_struct_with_ptr_caller() {
|
void returned_ptr_to_struct_with_ptr_caller() {
|
||||||
S* s = returned_ptr_to_struct_with_ptr_callee();
|
S* s = returned_ptr_to_struct_with_ptr_callee();
|
||||||
@ -877,3 +948,137 @@ void top_malloc_no_crash_fn() {
|
|||||||
free(pptr);
|
free(pptr);
|
||||||
}
|
}
|
||||||
} // namespace alloca_region_pointer
|
} // namespace alloca_region_pointer
|
||||||
|
|
||||||
|
// These all warn with -Wreturn-stack-address also
|
||||||
|
namespace return_address_of_true_positives {
|
||||||
|
int* ret_local_addrOf() {
|
||||||
|
int x = 1;
|
||||||
|
return &*&x; // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_local_addrOf_paren() {
|
||||||
|
int x = 1;
|
||||||
|
return (&(*(&x))); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_local_addrOf_ptr_arith() {
|
||||||
|
int x = 1;
|
||||||
|
return &*(&x+1); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_local_addrOf_ptr_arith2() {
|
||||||
|
int x = 1;
|
||||||
|
return &*(&x+1); // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int* ret_local_field() {
|
||||||
|
struct { int x; } a;
|
||||||
|
return &a.x; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}} expected-warning {{address of stack memory associated with local variable 'a' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int& ret_local_field_ref() {
|
||||||
|
struct { int x; } a;
|
||||||
|
return a.x; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}} expected-warning {{reference to stack memory associated with local variable 'a' returned}}
|
||||||
|
}
|
||||||
|
} //namespace return_address_of_true_positives
|
||||||
|
|
||||||
|
namespace true_negatives_return_expressions {
|
||||||
|
struct Container { int *x; };
|
||||||
|
|
||||||
|
int test2() {
|
||||||
|
int x = 14;
|
||||||
|
return x; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_void() {
|
||||||
|
return void(); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_assign_expr_safe() {
|
||||||
|
int y, x = 14;
|
||||||
|
return y = x; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_comma_separated_expressions_safe() {
|
||||||
|
int x = 1;
|
||||||
|
int *y;
|
||||||
|
return (y=&x), (x=15); // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_comma_separated_expressions_container_safe() {
|
||||||
|
int x = 1;
|
||||||
|
Container Other;
|
||||||
|
return Other = Container{&x}, x = 14; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int make_x();
|
||||||
|
int return_symbol_safe() {
|
||||||
|
int x = make_x();
|
||||||
|
clang_analyzer_dump(x); // expected-warning-re {{conj_$2{int, {{.+}}}}}
|
||||||
|
return x; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int *return_symbolic_region_safe(int **ptr) {
|
||||||
|
return *ptr; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int *return_arg_ptr(int *arg) {
|
||||||
|
return arg; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
// This has a false positive with -Wreturn-stack-address, but CSA should not
|
||||||
|
// warn on this
|
||||||
|
int *return_conditional_never_false(int *arg) {
|
||||||
|
int x = 14;
|
||||||
|
return true ? arg : &x; // expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This has a false positive with -Wreturn-stack-address, but CSA should not
|
||||||
|
// warn on this
|
||||||
|
int *return_conditional_never_true(int *arg) {
|
||||||
|
int x = 14;
|
||||||
|
return false ? &x : arg; // expected-warning {{address of stack memory associated with local variable 'x' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int *return_conditional_path_split(int *arg, bool b) {
|
||||||
|
int x = 14;
|
||||||
|
return b ? arg : &x; // expected-warning {{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning {{address of stack memory associated with local variable 'x' returned}} -Wreturn-stack-address
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare of two symbolic regions
|
||||||
|
// maybe in some implementation, make_ptr returns interior pointers that are comparable
|
||||||
|
int *make_ptr();
|
||||||
|
bool return_symbolic_exxpression() {
|
||||||
|
int *a = make_ptr();
|
||||||
|
int *b = make_ptr();
|
||||||
|
return a < b; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int *return_static() {
|
||||||
|
static int x = 0;
|
||||||
|
return &x; // no-warning
|
||||||
|
}
|
||||||
|
|
||||||
|
int* return_cpp_reinterpret_cast_no_warning(long x) {
|
||||||
|
return reinterpret_cast<int*>(x); // no-warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The tail call expression tree must not hold a reference to an arg or stack local:
|
||||||
|
// "The lifetimes of all local variables and function parameters end immediately before the
|
||||||
|
// [tail] call to the function. This means that it is undefined behaviour to pass a pointer or
|
||||||
|
// reference to a local variable to the called function, which is not the case without the
|
||||||
|
// attribute."
|
||||||
|
// These only warn from -Wreturn-stack-address for now
|
||||||
|
namespace with_attr_musttail {
|
||||||
|
void TakesIntAndPtr(int, int *);
|
||||||
|
void PassAddressOfLocal(int a, int *b) {
|
||||||
|
int c;
|
||||||
|
[[clang::musttail]] return TakesIntAndPtr(0, &c); // expected-warning {{address of stack memory associated with local variable 'c' pass\
|
||||||
|
ed to musttail function}} False-negative on CSA
|
||||||
|
}
|
||||||
|
void PassAddressOfParam(int a, int *b) {
|
||||||
|
[[clang::musttail]] return TakesIntAndPtr(0, &a); // expected-warning {{address of stack memory associated with parameter 'a' passed to\
|
||||||
|
musttail function}} False-negative on CSA
|
||||||
|
}
|
||||||
|
} // namespace with_attr_musttail
|
||||||
|
@ -56,3 +56,15 @@ void assignAsBool(void) {
|
|||||||
int x;
|
int x;
|
||||||
b = &x;
|
b = &x;
|
||||||
} // no-warning
|
} // no-warning
|
||||||
|
|
||||||
|
int *f(int* p __attribute__((lifetimebound)));
|
||||||
|
int *g() {
|
||||||
|
int i;
|
||||||
|
return f(&i); // expected-warning {{address of stack memory associated with local variable 'i' returned}}
|
||||||
|
}
|
||||||
|
|
||||||
|
int *f_no_lifetime_bound(int *p);
|
||||||
|
int *g_no_lifetime_bound() {
|
||||||
|
int i = 0;
|
||||||
|
return f_no_lifetime_bound(&i); // no-warning
|
||||||
|
}
|
||||||
|
@ -18,7 +18,7 @@ struct myfunction {
|
|||||||
myfunction create_func() {
|
myfunction create_func() {
|
||||||
int n;
|
int n;
|
||||||
auto c = [&n] {};
|
auto c = [&n] {};
|
||||||
return c; // expected-warning {{Address of stack memory associated with local variable 'n' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference}}
|
return c; // expected-warning {{Address of stack memory associated with local variable 'n' returned to caller}} expected-warning{{Address of stack memory associated with local variable 'n' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference}}
|
||||||
}
|
}
|
||||||
void gh_66221() {
|
void gh_66221() {
|
||||||
create_func()();
|
create_func()();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user