17330f729Sjoerg //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This file defines stack address leak checker, which checks if an invalid
107330f729Sjoerg // stack address is stored into a global or heap location. See CERT DCL30-C.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
157330f729Sjoerg #include "clang/AST/ExprCXX.h"
167330f729Sjoerg #include "clang/Basic/SourceManager.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
227330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
237330f729Sjoerg #include "llvm/ADT/SmallString.h"
247330f729Sjoerg #include "llvm/Support/raw_ostream.h"
257330f729Sjoerg using namespace clang;
267330f729Sjoerg using namespace ento;
277330f729Sjoerg
287330f729Sjoerg namespace {
297330f729Sjoerg class StackAddrEscapeChecker
307330f729Sjoerg : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
317330f729Sjoerg check::EndFunction> {
327330f729Sjoerg mutable IdentifierInfo *dispatch_semaphore_tII;
337330f729Sjoerg mutable std::unique_ptr<BuiltinBug> BT_stackleak;
347330f729Sjoerg mutable std::unique_ptr<BuiltinBug> BT_returnstack;
357330f729Sjoerg mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
367330f729Sjoerg mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
377330f729Sjoerg
387330f729Sjoerg public:
397330f729Sjoerg enum CheckKind {
407330f729Sjoerg CK_StackAddrEscapeChecker,
417330f729Sjoerg CK_StackAddrAsyncEscapeChecker,
427330f729Sjoerg CK_NumCheckKinds
437330f729Sjoerg };
447330f729Sjoerg
457330f729Sjoerg DefaultBool ChecksEnabled[CK_NumCheckKinds];
46*e038c9c4Sjoerg CheckerNameRef CheckNames[CK_NumCheckKinds];
477330f729Sjoerg
487330f729Sjoerg void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
497330f729Sjoerg void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
507330f729Sjoerg void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
517330f729Sjoerg
527330f729Sjoerg private:
537330f729Sjoerg void checkReturnedBlockCaptures(const BlockDataRegion &B,
547330f729Sjoerg CheckerContext &C) const;
557330f729Sjoerg void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
567330f729Sjoerg CheckerContext &C) const;
577330f729Sjoerg void EmitStackError(CheckerContext &C, const MemRegion *R,
587330f729Sjoerg const Expr *RetE) const;
597330f729Sjoerg bool isSemaphoreCaptured(const BlockDecl &B) const;
607330f729Sjoerg static SourceRange genName(raw_ostream &os, const MemRegion *R,
617330f729Sjoerg ASTContext &Ctx);
627330f729Sjoerg static SmallVector<const MemRegion *, 4>
637330f729Sjoerg getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
647330f729Sjoerg static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
657330f729Sjoerg static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
667330f729Sjoerg };
677330f729Sjoerg } // namespace
687330f729Sjoerg
genName(raw_ostream & os,const MemRegion * R,ASTContext & Ctx)697330f729Sjoerg SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
707330f729Sjoerg ASTContext &Ctx) {
717330f729Sjoerg // Get the base region, stripping away fields and elements.
727330f729Sjoerg R = R->getBaseRegion();
737330f729Sjoerg SourceManager &SM = Ctx.getSourceManager();
747330f729Sjoerg SourceRange range;
757330f729Sjoerg os << "Address of ";
767330f729Sjoerg
777330f729Sjoerg // Check if the region is a compound literal.
787330f729Sjoerg if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
797330f729Sjoerg const CompoundLiteralExpr *CL = CR->getLiteralExpr();
807330f729Sjoerg os << "stack memory associated with a compound literal "
817330f729Sjoerg "declared on line "
827330f729Sjoerg << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller";
837330f729Sjoerg range = CL->getSourceRange();
847330f729Sjoerg } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
857330f729Sjoerg const Expr *ARE = AR->getExpr();
867330f729Sjoerg SourceLocation L = ARE->getBeginLoc();
877330f729Sjoerg range = ARE->getSourceRange();
887330f729Sjoerg os << "stack memory allocated by call to alloca() on line "
897330f729Sjoerg << SM.getExpansionLineNumber(L);
907330f729Sjoerg } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
917330f729Sjoerg const BlockDecl *BD = BR->getCodeRegion()->getDecl();
927330f729Sjoerg SourceLocation L = BD->getBeginLoc();
937330f729Sjoerg range = BD->getSourceRange();
947330f729Sjoerg os << "stack-allocated block declared on line "
957330f729Sjoerg << SM.getExpansionLineNumber(L);
967330f729Sjoerg } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
977330f729Sjoerg os << "stack memory associated with local variable '" << VR->getString()
987330f729Sjoerg << '\'';
997330f729Sjoerg range = VR->getDecl()->getSourceRange();
1007330f729Sjoerg } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
1017330f729Sjoerg QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
1027330f729Sjoerg os << "stack memory associated with temporary object of type '";
1037330f729Sjoerg Ty.print(os, Ctx.getPrintingPolicy());
1047330f729Sjoerg os << "'";
1057330f729Sjoerg range = TOR->getExpr()->getSourceRange();
1067330f729Sjoerg } else {
1077330f729Sjoerg llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
1087330f729Sjoerg }
1097330f729Sjoerg
1107330f729Sjoerg return range;
1117330f729Sjoerg }
1127330f729Sjoerg
isArcManagedBlock(const MemRegion * R,CheckerContext & C)1137330f729Sjoerg bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
1147330f729Sjoerg CheckerContext &C) {
1157330f729Sjoerg assert(R && "MemRegion should not be null");
1167330f729Sjoerg return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
1177330f729Sjoerg isa<BlockDataRegion>(R);
1187330f729Sjoerg }
1197330f729Sjoerg
isNotInCurrentFrame(const MemRegion * R,CheckerContext & C)1207330f729Sjoerg bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
1217330f729Sjoerg CheckerContext &C) {
1227330f729Sjoerg const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
1237330f729Sjoerg return S->getStackFrame() != C.getStackFrame();
1247330f729Sjoerg }
1257330f729Sjoerg
isSemaphoreCaptured(const BlockDecl & B) const1267330f729Sjoerg bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
1277330f729Sjoerg if (!dispatch_semaphore_tII)
1287330f729Sjoerg dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
1297330f729Sjoerg for (const auto &C : B.captures()) {
1307330f729Sjoerg const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
1317330f729Sjoerg if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
1327330f729Sjoerg return true;
1337330f729Sjoerg }
1347330f729Sjoerg return false;
1357330f729Sjoerg }
1367330f729Sjoerg
1377330f729Sjoerg SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion & B,CheckerContext & C)1387330f729Sjoerg StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
1397330f729Sjoerg CheckerContext &C) {
1407330f729Sjoerg SmallVector<const MemRegion *, 4> Regions;
1417330f729Sjoerg BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
1427330f729Sjoerg BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
1437330f729Sjoerg for (; I != E; ++I) {
1447330f729Sjoerg SVal Val = C.getState()->getSVal(I.getCapturedRegion());
1457330f729Sjoerg const MemRegion *Region = Val.getAsRegion();
1467330f729Sjoerg if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
1477330f729Sjoerg Regions.push_back(Region);
1487330f729Sjoerg }
1497330f729Sjoerg return Regions;
1507330f729Sjoerg }
1517330f729Sjoerg
EmitStackError(CheckerContext & C,const MemRegion * R,const Expr * RetE) const1527330f729Sjoerg void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
1537330f729Sjoerg const MemRegion *R,
1547330f729Sjoerg const Expr *RetE) const {
1557330f729Sjoerg ExplodedNode *N = C.generateNonFatalErrorNode();
1567330f729Sjoerg if (!N)
1577330f729Sjoerg return;
1587330f729Sjoerg if (!BT_returnstack)
1597330f729Sjoerg BT_returnstack = std::make_unique<BuiltinBug>(
160*e038c9c4Sjoerg CheckNames[CK_StackAddrEscapeChecker],
161*e038c9c4Sjoerg "Return of address to stack-allocated memory");
1627330f729Sjoerg // Generate a report for this bug.
1637330f729Sjoerg SmallString<128> buf;
1647330f729Sjoerg llvm::raw_svector_ostream os(buf);
1657330f729Sjoerg SourceRange range = genName(os, R, C.getASTContext());
1667330f729Sjoerg os << " returned to caller";
1677330f729Sjoerg auto report =
1687330f729Sjoerg std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
1697330f729Sjoerg report->addRange(RetE->getSourceRange());
1707330f729Sjoerg if (range.isValid())
1717330f729Sjoerg report->addRange(range);
1727330f729Sjoerg C.emitReport(std::move(report));
1737330f729Sjoerg }
1747330f729Sjoerg
checkAsyncExecutedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const1757330f729Sjoerg void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
1767330f729Sjoerg const BlockDataRegion &B, CheckerContext &C) const {
1777330f729Sjoerg // There is a not-too-uncommon idiom
1787330f729Sjoerg // where a block passed to dispatch_async captures a semaphore
1797330f729Sjoerg // and then the thread (which called dispatch_async) is blocked on waiting
1807330f729Sjoerg // for the completion of the execution of the block
1817330f729Sjoerg // via dispatch_semaphore_wait. To avoid false-positives (for now)
1827330f729Sjoerg // we ignore all the blocks which have captured
1837330f729Sjoerg // a variable of the type "dispatch_semaphore_t".
1847330f729Sjoerg if (isSemaphoreCaptured(*B.getDecl()))
1857330f729Sjoerg return;
1867330f729Sjoerg for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
1877330f729Sjoerg // The block passed to dispatch_async may capture another block
1887330f729Sjoerg // created on the stack. However, there is no leak in this situaton,
1897330f729Sjoerg // no matter if ARC or no ARC is enabled:
1907330f729Sjoerg // dispatch_async copies the passed "outer" block (via Block_copy)
1917330f729Sjoerg // and if the block has captured another "inner" block,
1927330f729Sjoerg // the "inner" block will be copied as well.
1937330f729Sjoerg if (isa<BlockDataRegion>(Region))
1947330f729Sjoerg continue;
1957330f729Sjoerg ExplodedNode *N = C.generateNonFatalErrorNode();
1967330f729Sjoerg if (!N)
1977330f729Sjoerg continue;
1987330f729Sjoerg if (!BT_capturedstackasync)
1997330f729Sjoerg BT_capturedstackasync = std::make_unique<BuiltinBug>(
200*e038c9c4Sjoerg CheckNames[CK_StackAddrAsyncEscapeChecker],
201*e038c9c4Sjoerg "Address of stack-allocated memory is captured");
2027330f729Sjoerg SmallString<128> Buf;
2037330f729Sjoerg llvm::raw_svector_ostream Out(Buf);
2047330f729Sjoerg SourceRange Range = genName(Out, Region, C.getASTContext());
2057330f729Sjoerg Out << " is captured by an asynchronously-executed block";
2067330f729Sjoerg auto Report = std::make_unique<PathSensitiveBugReport>(
2077330f729Sjoerg *BT_capturedstackasync, Out.str(), N);
2087330f729Sjoerg if (Range.isValid())
2097330f729Sjoerg Report->addRange(Range);
2107330f729Sjoerg C.emitReport(std::move(Report));
2117330f729Sjoerg }
2127330f729Sjoerg }
2137330f729Sjoerg
checkReturnedBlockCaptures(const BlockDataRegion & B,CheckerContext & C) const2147330f729Sjoerg void StackAddrEscapeChecker::checkReturnedBlockCaptures(
2157330f729Sjoerg const BlockDataRegion &B, CheckerContext &C) const {
2167330f729Sjoerg for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
2177330f729Sjoerg if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
2187330f729Sjoerg continue;
2197330f729Sjoerg ExplodedNode *N = C.generateNonFatalErrorNode();
2207330f729Sjoerg if (!N)
2217330f729Sjoerg continue;
2227330f729Sjoerg if (!BT_capturedstackret)
2237330f729Sjoerg BT_capturedstackret = std::make_unique<BuiltinBug>(
224*e038c9c4Sjoerg CheckNames[CK_StackAddrEscapeChecker],
225*e038c9c4Sjoerg "Address of stack-allocated memory is captured");
2267330f729Sjoerg SmallString<128> Buf;
2277330f729Sjoerg llvm::raw_svector_ostream Out(Buf);
2287330f729Sjoerg SourceRange Range = genName(Out, Region, C.getASTContext());
2297330f729Sjoerg Out << " is captured by a returned block";
2307330f729Sjoerg auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
2317330f729Sjoerg Out.str(), N);
2327330f729Sjoerg if (Range.isValid())
2337330f729Sjoerg Report->addRange(Range);
2347330f729Sjoerg C.emitReport(std::move(Report));
2357330f729Sjoerg }
2367330f729Sjoerg }
2377330f729Sjoerg
checkPreCall(const CallEvent & Call,CheckerContext & C) const2387330f729Sjoerg void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
2397330f729Sjoerg CheckerContext &C) const {
2407330f729Sjoerg if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
2417330f729Sjoerg return;
2427330f729Sjoerg if (!Call.isGlobalCFunction("dispatch_after") &&
2437330f729Sjoerg !Call.isGlobalCFunction("dispatch_async"))
2447330f729Sjoerg return;
2457330f729Sjoerg for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
2467330f729Sjoerg if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
2477330f729Sjoerg Call.getArgSVal(Idx).getAsRegion()))
2487330f729Sjoerg checkAsyncExecutedBlockCaptures(*B, C);
2497330f729Sjoerg }
2507330f729Sjoerg }
2517330f729Sjoerg
checkPreStmt(const ReturnStmt * RS,CheckerContext & C) const2527330f729Sjoerg void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
2537330f729Sjoerg CheckerContext &C) const {
2547330f729Sjoerg if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2557330f729Sjoerg return;
2567330f729Sjoerg
2577330f729Sjoerg const Expr *RetE = RS->getRetValue();
2587330f729Sjoerg if (!RetE)
2597330f729Sjoerg return;
2607330f729Sjoerg RetE = RetE->IgnoreParens();
2617330f729Sjoerg
2627330f729Sjoerg SVal V = C.getSVal(RetE);
2637330f729Sjoerg const MemRegion *R = V.getAsRegion();
2647330f729Sjoerg if (!R)
2657330f729Sjoerg return;
2667330f729Sjoerg
2677330f729Sjoerg if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
2687330f729Sjoerg checkReturnedBlockCaptures(*B, C);
2697330f729Sjoerg
2707330f729Sjoerg if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
2717330f729Sjoerg isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
2727330f729Sjoerg return;
2737330f729Sjoerg
2747330f729Sjoerg // Returning a record by value is fine. (In this case, the returned
2757330f729Sjoerg // expression will be a copy-constructor, possibly wrapped in an
2767330f729Sjoerg // ExprWithCleanups node.)
2777330f729Sjoerg if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
2787330f729Sjoerg RetE = Cleanup->getSubExpr();
2797330f729Sjoerg if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
2807330f729Sjoerg return;
2817330f729Sjoerg
2827330f729Sjoerg // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
2837330f729Sjoerg // so the stack address is not escaping here.
284*e038c9c4Sjoerg if (const auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
2857330f729Sjoerg if (isa<BlockDataRegion>(R) &&
2867330f729Sjoerg ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
2877330f729Sjoerg return;
2887330f729Sjoerg }
2897330f729Sjoerg }
2907330f729Sjoerg
2917330f729Sjoerg EmitStackError(C, R, RetE);
2927330f729Sjoerg }
2937330f729Sjoerg
checkEndFunction(const ReturnStmt * RS,CheckerContext & Ctx) const2947330f729Sjoerg void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
2957330f729Sjoerg CheckerContext &Ctx) const {
2967330f729Sjoerg if (!ChecksEnabled[CK_StackAddrEscapeChecker])
2977330f729Sjoerg return;
2987330f729Sjoerg
2997330f729Sjoerg ProgramStateRef State = Ctx.getState();
3007330f729Sjoerg
3017330f729Sjoerg // Iterate over all bindings to global variables and see if it contains
3027330f729Sjoerg // a memory region in the stack space.
3037330f729Sjoerg class CallBack : public StoreManager::BindingsHandler {
3047330f729Sjoerg private:
3057330f729Sjoerg CheckerContext &Ctx;
3067330f729Sjoerg const StackFrameContext *CurSFC;
3077330f729Sjoerg
3087330f729Sjoerg public:
3097330f729Sjoerg SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
3107330f729Sjoerg
3117330f729Sjoerg CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
3127330f729Sjoerg
3137330f729Sjoerg bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
3147330f729Sjoerg SVal Val) override {
3157330f729Sjoerg
3167330f729Sjoerg if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
3177330f729Sjoerg return true;
3187330f729Sjoerg const MemRegion *VR = Val.getAsRegion();
3197330f729Sjoerg if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
3207330f729Sjoerg !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
3217330f729Sjoerg V.emplace_back(Region, VR);
3227330f729Sjoerg return true;
3237330f729Sjoerg }
3247330f729Sjoerg };
3257330f729Sjoerg
3267330f729Sjoerg CallBack Cb(Ctx);
3277330f729Sjoerg State->getStateManager().getStoreManager().iterBindings(State->getStore(),
3287330f729Sjoerg Cb);
3297330f729Sjoerg
3307330f729Sjoerg if (Cb.V.empty())
3317330f729Sjoerg return;
3327330f729Sjoerg
3337330f729Sjoerg // Generate an error node.
3347330f729Sjoerg ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
3357330f729Sjoerg if (!N)
3367330f729Sjoerg return;
3377330f729Sjoerg
3387330f729Sjoerg if (!BT_stackleak)
3397330f729Sjoerg BT_stackleak = std::make_unique<BuiltinBug>(
340*e038c9c4Sjoerg CheckNames[CK_StackAddrEscapeChecker],
341*e038c9c4Sjoerg "Stack address stored into global variable",
3427330f729Sjoerg "Stack address was saved into a global variable. "
3437330f729Sjoerg "This is dangerous because the address will become "
3447330f729Sjoerg "invalid after returning from the function");
3457330f729Sjoerg
3467330f729Sjoerg for (const auto &P : Cb.V) {
3477330f729Sjoerg // Generate a report for this bug.
3487330f729Sjoerg SmallString<128> Buf;
3497330f729Sjoerg llvm::raw_svector_ostream Out(Buf);
3507330f729Sjoerg SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
3517330f729Sjoerg Out << " is still referred to by the ";
3527330f729Sjoerg if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
3537330f729Sjoerg Out << "static";
3547330f729Sjoerg else
3557330f729Sjoerg Out << "global";
3567330f729Sjoerg Out << " variable '";
3577330f729Sjoerg const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
3587330f729Sjoerg Out << *VR->getDecl()
3597330f729Sjoerg << "' upon returning to the caller. This will be a dangling reference";
3607330f729Sjoerg auto Report =
3617330f729Sjoerg std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
3627330f729Sjoerg if (Range.isValid())
3637330f729Sjoerg Report->addRange(Range);
3647330f729Sjoerg
3657330f729Sjoerg Ctx.emitReport(std::move(Report));
3667330f729Sjoerg }
3677330f729Sjoerg }
3687330f729Sjoerg
registerStackAddrEscapeBase(CheckerManager & mgr)3697330f729Sjoerg void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
3707330f729Sjoerg mgr.registerChecker<StackAddrEscapeChecker>();
3717330f729Sjoerg }
3727330f729Sjoerg
shouldRegisterStackAddrEscapeBase(const CheckerManager & mgr)373*e038c9c4Sjoerg bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {
3747330f729Sjoerg return true;
3757330f729Sjoerg }
3767330f729Sjoerg
3777330f729Sjoerg #define REGISTER_CHECKER(name) \
3787330f729Sjoerg void ento::register##name(CheckerManager &Mgr) { \
379*e038c9c4Sjoerg StackAddrEscapeChecker *Chk = Mgr.getChecker<StackAddrEscapeChecker>(); \
3807330f729Sjoerg Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
381*e038c9c4Sjoerg Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \
382*e038c9c4Sjoerg Mgr.getCurrentCheckerName(); \
3837330f729Sjoerg } \
3847330f729Sjoerg \
385*e038c9c4Sjoerg bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
3867330f729Sjoerg
3877330f729Sjoerg REGISTER_CHECKER(StackAddrEscapeChecker)
3887330f729Sjoerg REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
389