head	1.1;
branch	1.1.1;
access;
symbols
	netbsd-11-0-RC4:1.1.1.7
	netbsd-11-0-RC3:1.1.1.7
	netbsd-11-0-RC2:1.1.1.7
	netbsd-11-0-RC1:1.1.1.7
	perseant-exfatfs-base-20250801:1.1.1.7
	netbsd-11:1.1.1.7.0.10
	netbsd-11-base:1.1.1.7
	netbsd-10-1-RELEASE:1.1.1.7
	perseant-exfatfs-base-20240630:1.1.1.7
	perseant-exfatfs:1.1.1.7.0.8
	perseant-exfatfs-base:1.1.1.7
	netbsd-8-3-RELEASE:1.1.1.5
	netbsd-9-4-RELEASE:1.1.1.6
	netbsd-10-0-RELEASE:1.1.1.7
	netbsd-10-0-RC6:1.1.1.7
	netbsd-10-0-RC5:1.1.1.7
	netbsd-10-0-RC4:1.1.1.7
	netbsd-10-0-RC3:1.1.1.7
	netbsd-10-0-RC2:1.1.1.7
	netbsd-10-0-RC1:1.1.1.7
	netbsd-10:1.1.1.7.0.6
	netbsd-10-base:1.1.1.7
	netbsd-9-3-RELEASE:1.1.1.6
	cjep_sun2x:1.1.1.7.0.4
	cjep_sun2x-base:1.1.1.7
	cjep_staticlib_x-base1:1.1.1.7
	netbsd-9-2-RELEASE:1.1.1.6
	cjep_staticlib_x:1.1.1.7.0.2
	cjep_staticlib_x-base:1.1.1.7
	netbsd-9-1-RELEASE:1.1.1.6
	phil-wifi-20200421:1.1.1.7
	phil-wifi-20200411:1.1.1.7
	phil-wifi-20200406:1.1.1.7
	netbsd-8-2-RELEASE:1.1.1.5
	netbsd-9-0-RELEASE:1.1.1.6
	netbsd-9-0-RC2:1.1.1.6
	netbsd-9-0-RC1:1.1.1.6
	netbsd-9:1.1.1.6.0.2
	netbsd-9-base:1.1.1.6
	phil-wifi-20190609:1.1.1.6
	netbsd-8-1-RELEASE:1.1.1.5
	netbsd-8-1-RC1:1.1.1.5
	pgoyette-compat-merge-20190127:1.1.1.5.12.1
	pgoyette-compat-20190127:1.1.1.6
	pgoyette-compat-20190118:1.1.1.6
	pgoyette-compat-1226:1.1.1.6
	pgoyette-compat-1126:1.1.1.6
	pgoyette-compat-1020:1.1.1.6
	pgoyette-compat-0930:1.1.1.6
	pgoyette-compat-0906:1.1.1.6
	netbsd-7-2-RELEASE:1.1.1.3
	pgoyette-compat-0728:1.1.1.6
	clang-337282:1.1.1.6
	netbsd-8-0-RELEASE:1.1.1.5
	phil-wifi:1.1.1.5.0.14
	phil-wifi-base:1.1.1.5
	pgoyette-compat-0625:1.1.1.5
	netbsd-8-0-RC2:1.1.1.5
	pgoyette-compat-0521:1.1.1.5
	pgoyette-compat-0502:1.1.1.5
	pgoyette-compat-0422:1.1.1.5
	netbsd-8-0-RC1:1.1.1.5
	pgoyette-compat-0415:1.1.1.5
	pgoyette-compat-0407:1.1.1.5
	pgoyette-compat-0330:1.1.1.5
	pgoyette-compat-0322:1.1.1.5
	pgoyette-compat-0315:1.1.1.5
	netbsd-7-1-2-RELEASE:1.1.1.3
	pgoyette-compat:1.1.1.5.0.12
	pgoyette-compat-base:1.1.1.5
	netbsd-7-1-1-RELEASE:1.1.1.3
	clang-319952:1.1.1.5
	matt-nb8-mediatek:1.1.1.5.0.10
	matt-nb8-mediatek-base:1.1.1.5
	clang-309604:1.1.1.5
	perseant-stdc-iso10646:1.1.1.5.0.8
	perseant-stdc-iso10646-base:1.1.1.5
	netbsd-8:1.1.1.5.0.6
	netbsd-8-base:1.1.1.5
	prg-localcount2-base3:1.1.1.5
	prg-localcount2-base2:1.1.1.5
	prg-localcount2-base1:1.1.1.5
	prg-localcount2:1.1.1.5.0.4
	prg-localcount2-base:1.1.1.5
	pgoyette-localcount-20170426:1.1.1.5
	bouyer-socketcan-base1:1.1.1.5
	pgoyette-localcount-20170320:1.1.1.5
	netbsd-7-1:1.1.1.3.0.10
	netbsd-7-1-RELEASE:1.1.1.3
	netbsd-7-1-RC2:1.1.1.3
	clang-294123:1.1.1.5
	netbsd-7-nhusb-base-20170116:1.1.1.3
	bouyer-socketcan:1.1.1.5.0.2
	bouyer-socketcan-base:1.1.1.5
	clang-291444:1.1.1.5
	pgoyette-localcount-20170107:1.1.1.4
	netbsd-7-1-RC1:1.1.1.3
	pgoyette-localcount-20161104:1.1.1.4
	netbsd-7-0-2-RELEASE:1.1.1.3
	localcount-20160914:1.1.1.4
	netbsd-7-nhusb:1.1.1.3.0.8
	netbsd-7-nhusb-base:1.1.1.3
	clang-280599:1.1.1.4
	pgoyette-localcount-20160806:1.1.1.4
	pgoyette-localcount-20160726:1.1.1.4
	pgoyette-localcount:1.1.1.4.0.2
	pgoyette-localcount-base:1.1.1.4
	netbsd-7-0-1-RELEASE:1.1.1.3
	clang-261930:1.1.1.4
	netbsd-7-0:1.1.1.3.0.6
	netbsd-7-0-RELEASE:1.1.1.3
	netbsd-7-0-RC3:1.1.1.3
	netbsd-7-0-RC2:1.1.1.3
	netbsd-7-0-RC1:1.1.1.3
	clang-237755:1.1.1.3
	clang-232565:1.1.1.3
	clang-227398:1.1.1.3
	tls-maxphys-base:1.1.1.3
	tls-maxphys:1.1.1.3.0.4
	netbsd-7:1.1.1.3.0.2
	netbsd-7-base:1.1.1.3
	clang-215315:1.1.1.3
	clang-209886:1.1.1.3
	yamt-pagecache:1.1.1.2.0.4
	yamt-pagecache-base9:1.1.1.2
	tls-earlyentropy:1.1.1.2.0.2
	tls-earlyentropy-base:1.1.1.3
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.1.1.2
	riastradh-drm2-base3:1.1.1.2
	clang-202566:1.1.1.2
	clang-201163:1.1.1.1
	clang-199312:1.1.1.1
	clang-198450:1.1.1.1
	clang-196603:1.1.1.1
	clang-195771:1.1.1.1
	LLVM:1.1.1;
locks; strict;
comment	@// @;


1.1
date	2013.11.28.14.14.52;	author joerg;	state Exp;
branches
	1.1.1.1;
next	;
commitid	ow8OybrawrB1f3fx;

1.1.1.1
date	2013.11.28.14.14.52;	author joerg;	state Exp;
branches;
next	1.1.1.2;
commitid	ow8OybrawrB1f3fx;

1.1.1.2
date	2014.03.04.19.55.00;	author joerg;	state Exp;
branches
	1.1.1.2.2.1
	1.1.1.2.4.1;
next	1.1.1.3;
commitid	29z1hJonZISIXprx;

1.1.1.3
date	2014.05.30.18.14.44;	author joerg;	state Exp;
branches
	1.1.1.3.4.1;
next	1.1.1.4;
commitid	8q0kdlBlCn09GACx;

1.1.1.4
date	2016.02.27.22.12.06;	author joerg;	state Exp;
branches
	1.1.1.4.2.1;
next	1.1.1.5;
commitid	tIimz3oDlh1NpBWy;

1.1.1.5
date	2017.01.11.10.35.41;	author joerg;	state Exp;
branches
	1.1.1.5.12.1
	1.1.1.5.14.1;
next	1.1.1.6;
commitid	CNnUNfII1jgNmxBz;

1.1.1.6
date	2018.07.17.18.31.07;	author joerg;	state Exp;
branches;
next	1.1.1.7;
commitid	wDzL46ALjrCZgwKA;

1.1.1.7
date	2019.11.13.22.19.28;	author joerg;	state dead;
branches;
next	;
commitid	QD8YATxuNG34YJKB;

1.1.1.2.2.1
date	2014.08.10.07.08.10;	author tls;	state Exp;
branches;
next	;
commitid	t01A1TLTYxkpGMLx;

1.1.1.2.4.1
date	2014.03.04.19.55.00;	author yamt;	state dead;
branches;
next	1.1.1.2.4.2;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.2.4.2
date	2014.05.22.16.18.31;	author yamt;	state Exp;
branches;
next	;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.3.4.1
date	2014.05.30.18.14.44;	author tls;	state dead;
branches;
next	1.1.1.3.4.2;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.3.4.2
date	2014.08.19.23.47.31;	author tls;	state Exp;
branches;
next	;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.4.2.1
date	2017.03.20.06.52.42;	author pgoyette;	state Exp;
branches;
next	;
commitid	jjw7cAwgyKq7RfKz;

1.1.1.5.12.1
date	2018.07.28.04.33.24;	author pgoyette;	state Exp;
branches;
next	;
commitid	1UP1xAIUxv1ZgRLA;

1.1.1.5.14.1
date	2019.06.10.21.45.28;	author christos;	state Exp;
branches;
next	1.1.1.5.14.2;
commitid	jtc8rnCzWiEEHGqB;

1.1.1.5.14.2
date	2020.04.13.07.46.39;	author martin;	state dead;
branches;
next	;
commitid	X01YhRUPVUDaec4C;


desc
@@


1.1
log
@Initial revision
@
text
@//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines stack address leak checker, which checks if an invalid 
// stack address is stored into a global or heap location. See CERT DCL30-C.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;

namespace {
class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
                                               check::EndFunction > {
  mutable OwningPtr<BuiltinBug> BT_stackleak;
  mutable OwningPtr<BuiltinBug> BT_returnstack;

public:
  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
  void checkEndFunction(CheckerContext &Ctx) const;
private:
  void EmitStackError(CheckerContext &C, const MemRegion *R,
                      const Expr *RetE) const;
  static SourceRange genName(raw_ostream &os, const MemRegion *R,
                             ASTContext &Ctx);
};
}

SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
                                            ASTContext &Ctx) {
    // Get the base region, stripping away fields and elements.
  R = R->getBaseRegion();
  SourceManager &SM = Ctx.getSourceManager();
  SourceRange range;
  os << "Address of ";
  
  // Check if the region is a compound literal.
  if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 
    const CompoundLiteralExpr *CL = CR->getLiteralExpr();
    os << "stack memory associated with a compound literal "
          "declared on line "
        << SM.getExpansionLineNumber(CL->getLocStart())
        << " returned to caller";    
    range = CL->getSourceRange();
  }
  else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
    const Expr *ARE = AR->getExpr();
    SourceLocation L = ARE->getLocStart();
    range = ARE->getSourceRange();    
    os << "stack memory allocated by call to alloca() on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
    const BlockDecl *BD = BR->getCodeRegion()->getDecl();
    SourceLocation L = BD->getLocStart();
    range = BD->getSourceRange();
    os << "stack-allocated block declared on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '"
       << VR->getString() << '\'';
    range = VR->getDecl()->getSourceRange();
  }
  else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
    QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
    os << "stack memory associated with temporary object of type '";
    Ty.print(os, Ctx.getPrintingPolicy());
    os << "'";
    range = TOR->getExpr()->getSourceRange();
  }
  else {
    llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
  } 
  
  return range;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
                                          const Expr *RetE) const {
  ExplodedNode *N = C.generateSink();

  if (!N)
    return;

  if (!BT_returnstack)
   BT_returnstack.reset(
                 new BuiltinBug("Return of address to stack-allocated memory"));

  // Generate a report for this bug.
  SmallString<512> buf;
  llvm::raw_svector_ostream os(buf);
  SourceRange range = genName(os, R, C.getASTContext());
  os << " returned to caller";
  BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
  report->addRange(RetE->getSourceRange());
  if (range.isValid())
    report->addRange(range);

  C.emitReport(report);
}

void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
                                          CheckerContext &C) const {
  
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  RetE = RetE->IgnoreParens();

  const LocationContext *LCtx = C.getLocationContext();
  SVal V = C.getState()->getSVal(RetE, LCtx);
  const MemRegion *R = V.getAsRegion();

  if (!R)
    return;
  
  const StackSpaceRegion *SS =
    dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
    
  if (!SS)
    return;

  // Return stack memory in an ancestor stack frame is fine.
  const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame();
  const StackFrameContext *MemFrame = SS->getStackFrame();
  if (MemFrame != CurFrame)
    return;

  // Automatic reference counting automatically copies blocks.
  if (C.getASTContext().getLangOpts().ObjCAutoRefCount &&
      isa<BlockDataRegion>(R))
    return;

  // 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;

  EmitStackError(C, R, RetE);
}

void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
  ProgramStateRef state = Ctx.getState();

  // Iterate over all bindings to global variables and see if it contains
  // a memory region in the stack space.
  class CallBack : public StoreManager::BindingsHandler {
  private:
    CheckerContext &Ctx;
    const StackFrameContext *CurSFC;
  public:
    SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;

    CallBack(CheckerContext &CC) :
      Ctx(CC),
      CurSFC(CC.getLocationContext()->getCurrentStackFrame())
    {}
    
    bool HandleBinding(StoreManager &SMgr, Store store,
                       const MemRegion *region, SVal val) {
      
      if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
        return true;
      
      const MemRegion *vR = val.getAsRegion();
      if (!vR)
        return true;
        
      // Under automated retain release, it is okay to assign a block
      // directly to a global variable.
      if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
          isa<BlockDataRegion>(vR))
        return true;

      if (const StackSpaceRegion *SSR = 
          dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
        // If the global variable holds a location in the current stack frame,
        // record the binding to emit a warning.
        if (SSR->getStackFrame() == CurSFC)
          V.push_back(std::make_pair(region, vR));
      }
      
      return true;
    }
  };
    
  CallBack cb(Ctx);
  state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);

  if (cb.V.empty())
    return;

  // Generate an error node.
  ExplodedNode *N = Ctx.addTransition(state);
  if (!N)
    return;

  if (!BT_stackleak)
    BT_stackleak.reset(
      new BuiltinBug("Stack address stored into global variable",
                     "Stack address was saved into a global variable. "
                     "This is dangerous because the address will become "
                     "invalid after returning from the function"));
  
  for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
    // Generate a report for this bug.
    SmallString<512> buf;
    llvm::raw_svector_ostream os(buf);
    SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext());
    os << " is still referred to by the global variable '";
    const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
    os << *VR->getDecl()
       << "' upon returning to the caller.  This will be a dangling reference";
    BugReport *report = new BugReport(*BT_stackleak, os.str(), N);
    if (range.isValid())
      report->addRange(range);

    Ctx.emitReport(report);
  }
}

void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) {
  mgr.registerChecker<StackAddrEscapeChecker>();
}
@


1.1.1.1
log
@Import Clang 3.4rc1 r195771.
@
text
@@


1.1.1.2
log
@Import Clang 3.5svn r202566.
@
text
@d103 2
a104 2
    BT_returnstack.reset(
        new BuiltinBug(this, "Return of address to stack-allocated memory"));
d220 5
a224 5
        new BuiltinBug(this, "Stack address stored into global variable",
                       "Stack address was saved into a global variable. "
                       "This is dangerous because the address will become "
                       "invalid after returning from the function"));

@


1.1.1.2.2.1
log
@Rebase.
@
text
@d31 2
a32 2
  mutable std::unique_ptr<BuiltinBug> BT_stackleak;
  mutable std::unique_ptr<BuiltinBug> BT_returnstack;
d180 2
a181 2
                       const MemRegion *region, SVal val) override {

@


1.1.1.3
log
@Import Clang 3.5svn r209886.
@
text
@d31 2
a32 2
  mutable std::unique_ptr<BuiltinBug> BT_stackleak;
  mutable std::unique_ptr<BuiltinBug> BT_returnstack;
d180 2
a181 2
                       const MemRegion *region, SVal val) override {

@


1.1.1.4
log
@Import Clang 3.8.0rc3 r261930.
@
text
@d10 1
a10 1
// This file defines stack address leak checker, which checks if an invalid
d52 1
a52 1

d54 1
a54 1
  if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
d59 1
a59 1
        << " returned to caller";
d65 1
a65 1
    range = ARE->getSourceRange();
d90 2
a91 2
  }

d97 1
a97 1
  ExplodedNode *N = C.generateErrorNode();
d111 1
a111 1
  auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N);
d116 1
a116 1
  C.emitReport(std::move(report));
d121 1
a121 1

d133 1
a133 1

d136 1
a136 1

a158 9
  // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
  // so the stack address is not escaping here.
  if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
    if (isa<BlockDataRegion>(R) &&
        ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
      return;
    }
  }

d178 1
a178 1

d184 1
a184 1

d188 1
a188 1

d195 1
a195 1
      if (const StackSpaceRegion *SSR =
d202 1
a202 1

d206 1
a206 1

d214 1
a214 1
  ExplodedNode *N = Ctx.generateNonFatalErrorNode(state);
d234 1
a234 1
    auto report = llvm::make_unique<BugReport>(*BT_stackleak, os.str(), N);
d238 1
a238 1
    Ctx.emitReport(std::move(report));
@


1.1.1.4.2.1
log
@Sync with HEAD
@
text
@d239 1
a239 6
    os << " is still referred to by the ";
    if (isa<StaticGlobalSpaceRegion>(cb.V[i].first->getMemorySpace()))
      os << "static";
    else
      os << "global";
    os << " variable '";
@


1.1.1.5
log
@Import Clang pre-4.0.0 r291444.
@
text
@d239 1
a239 6
    os << " is still referred to by the ";
    if (isa<StaticGlobalSpaceRegion>(cb.V[i].first->getMemorySpace()))
      os << "static";
    else
      os << "global";
    os << " variable '";
@


1.1.1.5.14.1
log
@Sync with HEAD
@
text
@a20 1
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
d29 2
a30 4
class StackAddrEscapeChecker
    : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
                     check::EndFunction> {
  mutable IdentifierInfo *dispatch_semaphore_tII;
a32 2
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
a34 9
  enum CheckKind {
    CK_StackAddrEscapeChecker,
    CK_StackAddrAsyncEscapeChecker,
    CK_NumCheckKinds
  };

  DefaultBool ChecksEnabled[CK_NumCheckKinds];

  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
d36 1
a36 2
  void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;

a37 4
  void checkReturnedBlockCaptures(const BlockDataRegion &B,
                                  CheckerContext &C) const;
  void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
                                       CheckerContext &C) const;
a39 1
  bool isSemaphoreCaptured(const BlockDecl &B) const;
a41 4
  static SmallVector<const MemRegion *, 4>
  getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
  static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
  static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
d43 1
a43 1
} // namespace
d47 1
a47 1
  // Get the base region, stripping away fields and elements.
d54 1
a54 1
  if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
d58 2
a59 1
       << SM.getExpansionLineNumber(CL->getLocStart()) << " returned to caller";
d61 2
a62 1
  } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
d68 2
a69 1
  } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
d75 4
a78 3
  } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '" << VR->getString()
       << '\'';
d80 2
a81 1
  } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
d87 2
a88 1
  } else {
d95 3
a97 6
bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
                                               CheckerContext &C) {
  assert(R && "MemRegion should not be null");
  return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
         isa<BlockDataRegion>(R);
}
a98 36
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
                                                 CheckerContext &C) {
  const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
  return S->getStackFrame() != C.getStackFrame();
}

bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
  if (!dispatch_semaphore_tII)
    dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
  for (const auto &C : B.captures()) {
    const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
    if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
      return true; 
  }
  return false;
}

SmallVector<const MemRegion *, 4>
StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
                                                CheckerContext &C) {
  SmallVector<const MemRegion *, 4> Regions;
  BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
  BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
  for (; I != E; ++I) {
    SVal Val = C.getState()->getSVal(I.getCapturedRegion());
    const MemRegion *Region = Val.getAsRegion();
    if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
      Regions.push_back(Region);
  }
  return Regions;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
                                            const MemRegion *R,
                                            const Expr *RetE) const {
  ExplodedNode *N = C.generateNonFatalErrorNode();
d101 1
d103 3
a105 2
    BT_returnstack = llvm::make_unique<BuiltinBug>(
        this, "Return of address to stack-allocated memory");
d107 1
a107 1
  SmallString<128> buf;
d115 1
a118 75
void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  // There is a not-too-uncommon idiom
  // where a block passed to dispatch_async captures a semaphore
  // and then the thread (which called dispatch_async) is blocked on waiting
  // for the completion of the execution of the block 
  // via dispatch_semaphore_wait. To avoid false-positives (for now) 
  // we ignore all the blocks which have captured 
  // a variable of the type "dispatch_semaphore_t".
  if (isSemaphoreCaptured(*B.getDecl()))
    return;
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    // The block passed to dispatch_async may capture another block
    // created on the stack. However, there is no leak in this situaton,
    // no matter if ARC or no ARC is enabled:
    // dispatch_async copies the passed "outer" block (via Block_copy)
    // and if the block has captured another "inner" block,
    // the "inner" block will be copied as well.
    if (isa<BlockDataRegion>(Region))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackasync)
      BT_capturedstackasync = llvm::make_unique<BuiltinBug>(
          this, "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 an asynchronously-executed block";
    auto Report =
        llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkReturnedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackret)
      BT_capturedstackret = llvm::make_unique<BuiltinBug>(
          this, "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 =
        llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
                                          CheckerContext &C) const {
  if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
    return;
  if (!Call.isGlobalCFunction("dispatch_after") &&
      !Call.isGlobalCFunction("dispatch_async"))
    return;
  for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
    if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
            Call.getArgSVal(Idx).getAsRegion()))
      checkAsyncExecutedBlockCaptures(*B, C);
  }
}

a120 2
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;
d127 2
a128 1
  SVal V = C.getSVal(RetE);
d130 1
d134 11
a144 2
  if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
    checkReturnedBlockCaptures(*B, C);
d146 3
a148 2
  if (!isa<StackSpaceRegion>(R->getMemorySpace()) || 
      isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
d171 2
a172 6
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
                                              CheckerContext &Ctx) const {
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;

  ProgramStateRef State = Ctx.getState();
d180 2
d183 4
a186 2
  public:
    SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
d188 2
a189 1
    CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
d191 2
a192 2
    bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
                       SVal Val) override {
d194 2
a195 1
      if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
d197 15
a211 4
      const MemRegion *VR = Val.getAsRegion();
      if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
          !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
        V.emplace_back(Region, VR);
d216 2
a217 3
  CallBack Cb(Ctx);
  State->getStateManager().getStoreManager().iterBindings(State->getStore(),
                                                          Cb);
d219 1
a219 1
  if (Cb.V.empty())
d223 1
a223 1
  ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
d228 5
a232 5
    BT_stackleak = llvm::make_unique<BuiltinBug>(
        this, "Stack address stored into global variable",
        "Stack address was saved into a global variable. "
        "This is dangerous because the address will become "
        "invalid after returning from the function");
d234 1
a234 1
  for (const auto &P : Cb.V) {
d236 6
a241 6
    SmallString<128> Buf;
    llvm::raw_svector_ostream Out(Buf);
    SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
    Out << " is still referred to by the ";
    if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
      Out << "static";
d243 8
a250 8
      Out << "global";
    Out << " variable '";
    const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
    Out << *VR->getDecl()
        << "' upon returning to the caller.  This will be a dangling reference";
    auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
d252 1
a252 1
    Ctx.emitReport(std::move(Report));
d256 3
a258 9
#define REGISTER_CHECKER(name) \
  void ento::register##name(CheckerManager &Mgr) { \
    StackAddrEscapeChecker *Chk = \
        Mgr.registerChecker<StackAddrEscapeChecker>(); \
    Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
  }

REGISTER_CHECKER(StackAddrEscapeChecker)
REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
@


1.1.1.5.14.2
log
@Mostly merge changes from HEAD upto 20200411
@
text
@@


1.1.1.5.12.1
log
@Sync with HEAD
@
text
@a20 1
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
d29 2
a30 4
class StackAddrEscapeChecker
    : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
                     check::EndFunction> {
  mutable IdentifierInfo *dispatch_semaphore_tII;
a32 2
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
a34 9
  enum CheckKind {
    CK_StackAddrEscapeChecker,
    CK_StackAddrAsyncEscapeChecker,
    CK_NumCheckKinds
  };

  DefaultBool ChecksEnabled[CK_NumCheckKinds];

  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
d36 1
a36 2
  void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;

a37 4
  void checkReturnedBlockCaptures(const BlockDataRegion &B,
                                  CheckerContext &C) const;
  void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
                                       CheckerContext &C) const;
a39 1
  bool isSemaphoreCaptured(const BlockDecl &B) const;
a41 4
  static SmallVector<const MemRegion *, 4>
  getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
  static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
  static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
d43 1
a43 1
} // namespace
d47 1
a47 1
  // Get the base region, stripping away fields and elements.
d54 1
a54 1
  if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
d58 2
a59 1
       << SM.getExpansionLineNumber(CL->getLocStart()) << " returned to caller";
d61 2
a62 1
  } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
d68 2
a69 1
  } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
d75 4
a78 3
  } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '" << VR->getString()
       << '\'';
d80 2
a81 1
  } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
d87 2
a88 1
  } else {
d95 3
a97 6
bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
                                               CheckerContext &C) {
  assert(R && "MemRegion should not be null");
  return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
         isa<BlockDataRegion>(R);
}
a98 36
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
                                                 CheckerContext &C) {
  const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
  return S->getStackFrame() != C.getStackFrame();
}

bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
  if (!dispatch_semaphore_tII)
    dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
  for (const auto &C : B.captures()) {
    const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
    if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
      return true; 
  }
  return false;
}

SmallVector<const MemRegion *, 4>
StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
                                                CheckerContext &C) {
  SmallVector<const MemRegion *, 4> Regions;
  BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
  BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
  for (; I != E; ++I) {
    SVal Val = C.getState()->getSVal(I.getCapturedRegion());
    const MemRegion *Region = Val.getAsRegion();
    if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
      Regions.push_back(Region);
  }
  return Regions;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
                                            const MemRegion *R,
                                            const Expr *RetE) const {
  ExplodedNode *N = C.generateNonFatalErrorNode();
d101 1
d103 3
a105 2
    BT_returnstack = llvm::make_unique<BuiltinBug>(
        this, "Return of address to stack-allocated memory");
d107 1
a107 1
  SmallString<128> buf;
d115 1
a118 75
void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  // There is a not-too-uncommon idiom
  // where a block passed to dispatch_async captures a semaphore
  // and then the thread (which called dispatch_async) is blocked on waiting
  // for the completion of the execution of the block 
  // via dispatch_semaphore_wait. To avoid false-positives (for now) 
  // we ignore all the blocks which have captured 
  // a variable of the type "dispatch_semaphore_t".
  if (isSemaphoreCaptured(*B.getDecl()))
    return;
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    // The block passed to dispatch_async may capture another block
    // created on the stack. However, there is no leak in this situaton,
    // no matter if ARC or no ARC is enabled:
    // dispatch_async copies the passed "outer" block (via Block_copy)
    // and if the block has captured another "inner" block,
    // the "inner" block will be copied as well.
    if (isa<BlockDataRegion>(Region))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackasync)
      BT_capturedstackasync = llvm::make_unique<BuiltinBug>(
          this, "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 an asynchronously-executed block";
    auto Report =
        llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkReturnedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackret)
      BT_capturedstackret = llvm::make_unique<BuiltinBug>(
          this, "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 =
        llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
                                          CheckerContext &C) const {
  if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
    return;
  if (!Call.isGlobalCFunction("dispatch_after") &&
      !Call.isGlobalCFunction("dispatch_async"))
    return;
  for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
    if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
            Call.getArgSVal(Idx).getAsRegion()))
      checkAsyncExecutedBlockCaptures(*B, C);
  }
}

a120 2
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;
d127 2
a128 1
  SVal V = C.getSVal(RetE);
d130 1
d134 11
a144 2
  if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
    checkReturnedBlockCaptures(*B, C);
d146 3
a148 2
  if (!isa<StackSpaceRegion>(R->getMemorySpace()) || 
      isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
d171 2
a172 6
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
                                              CheckerContext &Ctx) const {
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;

  ProgramStateRef State = Ctx.getState();
d180 2
d183 4
a186 2
  public:
    SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
d188 2
a189 1
    CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
d191 2
a192 2
    bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
                       SVal Val) override {
d194 2
a195 1
      if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
d197 15
a211 4
      const MemRegion *VR = Val.getAsRegion();
      if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
          !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
        V.emplace_back(Region, VR);
d216 2
a217 3
  CallBack Cb(Ctx);
  State->getStateManager().getStoreManager().iterBindings(State->getStore(),
                                                          Cb);
d219 1
a219 1
  if (Cb.V.empty())
d223 1
a223 1
  ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
d228 5
a232 5
    BT_stackleak = llvm::make_unique<BuiltinBug>(
        this, "Stack address stored into global variable",
        "Stack address was saved into a global variable. "
        "This is dangerous because the address will become "
        "invalid after returning from the function");
d234 1
a234 1
  for (const auto &P : Cb.V) {
d236 6
a241 6
    SmallString<128> Buf;
    llvm::raw_svector_ostream Out(Buf);
    SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
    Out << " is still referred to by the ";
    if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
      Out << "static";
d243 8
a250 8
      Out << "global";
    Out << " variable '";
    const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
    Out << *VR->getDecl()
        << "' upon returning to the caller.  This will be a dangling reference";
    auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
d252 1
a252 1
    Ctx.emitReport(std::move(Report));
d256 3
a258 9
#define REGISTER_CHECKER(name) \
  void ento::register##name(CheckerManager &Mgr) { \
    StackAddrEscapeChecker *Chk = \
        Mgr.registerChecker<StackAddrEscapeChecker>(); \
    Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
  }

REGISTER_CHECKER(StackAddrEscapeChecker)
REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
@


1.1.1.6
log
@Import clang r337282 from trunk
@
text
@a20 1
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
d29 2
a30 4
class StackAddrEscapeChecker
    : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
                     check::EndFunction> {
  mutable IdentifierInfo *dispatch_semaphore_tII;
a32 2
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
  mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
a34 9
  enum CheckKind {
    CK_StackAddrEscapeChecker,
    CK_StackAddrAsyncEscapeChecker,
    CK_NumCheckKinds
  };

  DefaultBool ChecksEnabled[CK_NumCheckKinds];

  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
d36 1
a36 2
  void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;

a37 4
  void checkReturnedBlockCaptures(const BlockDataRegion &B,
                                  CheckerContext &C) const;
  void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
                                       CheckerContext &C) const;
a39 1
  bool isSemaphoreCaptured(const BlockDecl &B) const;
a41 4
  static SmallVector<const MemRegion *, 4>
  getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
  static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
  static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
d43 1
a43 1
} // namespace
d47 1
a47 1
  // Get the base region, stripping away fields and elements.
d54 1
a54 1
  if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
d58 2
a59 1
       << SM.getExpansionLineNumber(CL->getLocStart()) << " returned to caller";
d61 2
a62 1
  } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
d68 2
a69 1
  } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
d75 4
a78 3
  } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '" << VR->getString()
       << '\'';
d80 2
a81 1
  } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
d87 2
a88 1
  } else {
d95 3
a97 6
bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
                                               CheckerContext &C) {
  assert(R && "MemRegion should not be null");
  return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
         isa<BlockDataRegion>(R);
}
a98 36
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
                                                 CheckerContext &C) {
  const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
  return S->getStackFrame() != C.getStackFrame();
}

bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
  if (!dispatch_semaphore_tII)
    dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
  for (const auto &C : B.captures()) {
    const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
    if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
      return true; 
  }
  return false;
}

SmallVector<const MemRegion *, 4>
StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
                                                CheckerContext &C) {
  SmallVector<const MemRegion *, 4> Regions;
  BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
  BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
  for (; I != E; ++I) {
    SVal Val = C.getState()->getSVal(I.getCapturedRegion());
    const MemRegion *Region = Val.getAsRegion();
    if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
      Regions.push_back(Region);
  }
  return Regions;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
                                            const MemRegion *R,
                                            const Expr *RetE) const {
  ExplodedNode *N = C.generateNonFatalErrorNode();
d101 1
d103 3
a105 2
    BT_returnstack = llvm::make_unique<BuiltinBug>(
        this, "Return of address to stack-allocated memory");
d107 1
a107 1
  SmallString<128> buf;
d115 1
a118 75
void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  // There is a not-too-uncommon idiom
  // where a block passed to dispatch_async captures a semaphore
  // and then the thread (which called dispatch_async) is blocked on waiting
  // for the completion of the execution of the block 
  // via dispatch_semaphore_wait. To avoid false-positives (for now) 
  // we ignore all the blocks which have captured 
  // a variable of the type "dispatch_semaphore_t".
  if (isSemaphoreCaptured(*B.getDecl()))
    return;
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    // The block passed to dispatch_async may capture another block
    // created on the stack. However, there is no leak in this situaton,
    // no matter if ARC or no ARC is enabled:
    // dispatch_async copies the passed "outer" block (via Block_copy)
    // and if the block has captured another "inner" block,
    // the "inner" block will be copied as well.
    if (isa<BlockDataRegion>(Region))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackasync)
      BT_capturedstackasync = llvm::make_unique<BuiltinBug>(
          this, "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 an asynchronously-executed block";
    auto Report =
        llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkReturnedBlockCaptures(
    const BlockDataRegion &B, CheckerContext &C) const {
  for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
    if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
      continue;
    ExplodedNode *N = C.generateNonFatalErrorNode();
    if (!N)
      continue;
    if (!BT_capturedstackret)
      BT_capturedstackret = llvm::make_unique<BuiltinBug>(
          this, "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 =
        llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
    C.emitReport(std::move(Report));
  }
}

void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
                                          CheckerContext &C) const {
  if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
    return;
  if (!Call.isGlobalCFunction("dispatch_after") &&
      !Call.isGlobalCFunction("dispatch_async"))
    return;
  for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
    if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
            Call.getArgSVal(Idx).getAsRegion()))
      checkAsyncExecutedBlockCaptures(*B, C);
  }
}

a120 2
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;
d127 2
a128 1
  SVal V = C.getSVal(RetE);
d130 1
d134 11
a144 2
  if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
    checkReturnedBlockCaptures(*B, C);
d146 3
a148 2
  if (!isa<StackSpaceRegion>(R->getMemorySpace()) || 
      isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
d171 2
a172 6
void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
                                              CheckerContext &Ctx) const {
  if (!ChecksEnabled[CK_StackAddrEscapeChecker])
    return;

  ProgramStateRef State = Ctx.getState();
d180 2
d183 4
a186 2
  public:
    SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
d188 2
a189 1
    CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
d191 2
a192 2
    bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
                       SVal Val) override {
d194 2
a195 1
      if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
d197 15
a211 4
      const MemRegion *VR = Val.getAsRegion();
      if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
          !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
        V.emplace_back(Region, VR);
d216 2
a217 3
  CallBack Cb(Ctx);
  State->getStateManager().getStoreManager().iterBindings(State->getStore(),
                                                          Cb);
d219 1
a219 1
  if (Cb.V.empty())
d223 1
a223 1
  ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
d228 5
a232 5
    BT_stackleak = llvm::make_unique<BuiltinBug>(
        this, "Stack address stored into global variable",
        "Stack address was saved into a global variable. "
        "This is dangerous because the address will become "
        "invalid after returning from the function");
d234 1
a234 1
  for (const auto &P : Cb.V) {
d236 6
a241 6
    SmallString<128> Buf;
    llvm::raw_svector_ostream Out(Buf);
    SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
    Out << " is still referred to by the ";
    if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
      Out << "static";
d243 8
a250 8
      Out << "global";
    Out << " variable '";
    const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
    Out << *VR->getDecl()
        << "' upon returning to the caller.  This will be a dangling reference";
    auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N);
    if (Range.isValid())
      Report->addRange(Range);
d252 1
a252 1
    Ctx.emitReport(std::move(Report));
d256 3
a258 9
#define REGISTER_CHECKER(name) \
  void ento::register##name(CheckerManager &Mgr) { \
    StackAddrEscapeChecker *Chk = \
        Mgr.registerChecker<StackAddrEscapeChecker>(); \
    Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
  }

REGISTER_CHECKER(StackAddrEscapeChecker)
REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
@


1.1.1.7
log
@Mark old LLVM instance as dead.
@
text
@@


1.1.1.3.4.1
log
@file StackAddrEscapeChecker.cpp was added on branch tls-maxphys on 2014-08-19 23:47:31 +0000
@
text
@d1 244
@


1.1.1.3.4.2
log
@Rebase to HEAD as of a few days ago.
@
text
@a0 244
//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines stack address leak checker, which checks if an invalid 
// stack address is stored into a global or heap location. See CERT DCL30-C.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;

namespace {
class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
                                               check::EndFunction > {
  mutable std::unique_ptr<BuiltinBug> BT_stackleak;
  mutable std::unique_ptr<BuiltinBug> BT_returnstack;

public:
  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
  void checkEndFunction(CheckerContext &Ctx) const;
private:
  void EmitStackError(CheckerContext &C, const MemRegion *R,
                      const Expr *RetE) const;
  static SourceRange genName(raw_ostream &os, const MemRegion *R,
                             ASTContext &Ctx);
};
}

SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
                                            ASTContext &Ctx) {
    // Get the base region, stripping away fields and elements.
  R = R->getBaseRegion();
  SourceManager &SM = Ctx.getSourceManager();
  SourceRange range;
  os << "Address of ";
  
  // Check if the region is a compound literal.
  if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 
    const CompoundLiteralExpr *CL = CR->getLiteralExpr();
    os << "stack memory associated with a compound literal "
          "declared on line "
        << SM.getExpansionLineNumber(CL->getLocStart())
        << " returned to caller";    
    range = CL->getSourceRange();
  }
  else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
    const Expr *ARE = AR->getExpr();
    SourceLocation L = ARE->getLocStart();
    range = ARE->getSourceRange();    
    os << "stack memory allocated by call to alloca() on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
    const BlockDecl *BD = BR->getCodeRegion()->getDecl();
    SourceLocation L = BD->getLocStart();
    range = BD->getSourceRange();
    os << "stack-allocated block declared on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '"
       << VR->getString() << '\'';
    range = VR->getDecl()->getSourceRange();
  }
  else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
    QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
    os << "stack memory associated with temporary object of type '";
    Ty.print(os, Ctx.getPrintingPolicy());
    os << "'";
    range = TOR->getExpr()->getSourceRange();
  }
  else {
    llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
  } 
  
  return range;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
                                          const Expr *RetE) const {
  ExplodedNode *N = C.generateSink();

  if (!N)
    return;

  if (!BT_returnstack)
    BT_returnstack.reset(
        new BuiltinBug(this, "Return of address to stack-allocated memory"));

  // Generate a report for this bug.
  SmallString<512> buf;
  llvm::raw_svector_ostream os(buf);
  SourceRange range = genName(os, R, C.getASTContext());
  os << " returned to caller";
  BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
  report->addRange(RetE->getSourceRange());
  if (range.isValid())
    report->addRange(range);

  C.emitReport(report);
}

void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
                                          CheckerContext &C) const {
  
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  RetE = RetE->IgnoreParens();

  const LocationContext *LCtx = C.getLocationContext();
  SVal V = C.getState()->getSVal(RetE, LCtx);
  const MemRegion *R = V.getAsRegion();

  if (!R)
    return;
  
  const StackSpaceRegion *SS =
    dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
    
  if (!SS)
    return;

  // Return stack memory in an ancestor stack frame is fine.
  const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame();
  const StackFrameContext *MemFrame = SS->getStackFrame();
  if (MemFrame != CurFrame)
    return;

  // Automatic reference counting automatically copies blocks.
  if (C.getASTContext().getLangOpts().ObjCAutoRefCount &&
      isa<BlockDataRegion>(R))
    return;

  // 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;

  EmitStackError(C, R, RetE);
}

void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
  ProgramStateRef state = Ctx.getState();

  // Iterate over all bindings to global variables and see if it contains
  // a memory region in the stack space.
  class CallBack : public StoreManager::BindingsHandler {
  private:
    CheckerContext &Ctx;
    const StackFrameContext *CurSFC;
  public:
    SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;

    CallBack(CheckerContext &CC) :
      Ctx(CC),
      CurSFC(CC.getLocationContext()->getCurrentStackFrame())
    {}
    
    bool HandleBinding(StoreManager &SMgr, Store store,
                       const MemRegion *region, SVal val) override {

      if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
        return true;
      
      const MemRegion *vR = val.getAsRegion();
      if (!vR)
        return true;
        
      // Under automated retain release, it is okay to assign a block
      // directly to a global variable.
      if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
          isa<BlockDataRegion>(vR))
        return true;

      if (const StackSpaceRegion *SSR = 
          dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
        // If the global variable holds a location in the current stack frame,
        // record the binding to emit a warning.
        if (SSR->getStackFrame() == CurSFC)
          V.push_back(std::make_pair(region, vR));
      }
      
      return true;
    }
  };
    
  CallBack cb(Ctx);
  state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);

  if (cb.V.empty())
    return;

  // Generate an error node.
  ExplodedNode *N = Ctx.addTransition(state);
  if (!N)
    return;

  if (!BT_stackleak)
    BT_stackleak.reset(
        new BuiltinBug(this, "Stack address stored into global variable",
                       "Stack address was saved into a global variable. "
                       "This is dangerous because the address will become "
                       "invalid after returning from the function"));

  for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
    // Generate a report for this bug.
    SmallString<512> buf;
    llvm::raw_svector_ostream os(buf);
    SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext());
    os << " is still referred to by the global variable '";
    const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
    os << *VR->getDecl()
       << "' upon returning to the caller.  This will be a dangling reference";
    BugReport *report = new BugReport(*BT_stackleak, os.str(), N);
    if (range.isValid())
      report->addRange(range);

    Ctx.emitReport(report);
  }
}

void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) {
  mgr.registerChecker<StackAddrEscapeChecker>();
}
@


1.1.1.2.4.1
log
@file StackAddrEscapeChecker.cpp was added on branch yamt-pagecache on 2014-05-22 16:18:31 +0000
@
text
@d1 244
@


1.1.1.2.4.2
log
@sync with head.

for a reference, the tree before this commit was tagged
as yamt-pagecache-tag8.

this commit was splitted into small chunks to avoid
a limitation of cvs.  ("Protocol error: too many arguments")
@
text
@a0 244
//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines stack address leak checker, which checks if an invalid 
// stack address is stored into a global or heap location. See CERT DCL30-C.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
using namespace ento;

namespace {
class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
                                               check::EndFunction > {
  mutable OwningPtr<BuiltinBug> BT_stackleak;
  mutable OwningPtr<BuiltinBug> BT_returnstack;

public:
  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
  void checkEndFunction(CheckerContext &Ctx) const;
private:
  void EmitStackError(CheckerContext &C, const MemRegion *R,
                      const Expr *RetE) const;
  static SourceRange genName(raw_ostream &os, const MemRegion *R,
                             ASTContext &Ctx);
};
}

SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
                                            ASTContext &Ctx) {
    // Get the base region, stripping away fields and elements.
  R = R->getBaseRegion();
  SourceManager &SM = Ctx.getSourceManager();
  SourceRange range;
  os << "Address of ";
  
  // Check if the region is a compound literal.
  if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) { 
    const CompoundLiteralExpr *CL = CR->getLiteralExpr();
    os << "stack memory associated with a compound literal "
          "declared on line "
        << SM.getExpansionLineNumber(CL->getLocStart())
        << " returned to caller";    
    range = CL->getSourceRange();
  }
  else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
    const Expr *ARE = AR->getExpr();
    SourceLocation L = ARE->getLocStart();
    range = ARE->getSourceRange();    
    os << "stack memory allocated by call to alloca() on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
    const BlockDecl *BD = BR->getCodeRegion()->getDecl();
    SourceLocation L = BD->getLocStart();
    range = BD->getSourceRange();
    os << "stack-allocated block declared on line "
       << SM.getExpansionLineNumber(L);
  }
  else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
    os << "stack memory associated with local variable '"
       << VR->getString() << '\'';
    range = VR->getDecl()->getSourceRange();
  }
  else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
    QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
    os << "stack memory associated with temporary object of type '";
    Ty.print(os, Ctx.getPrintingPolicy());
    os << "'";
    range = TOR->getExpr()->getSourceRange();
  }
  else {
    llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
  } 
  
  return range;
}

void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
                                          const Expr *RetE) const {
  ExplodedNode *N = C.generateSink();

  if (!N)
    return;

  if (!BT_returnstack)
    BT_returnstack.reset(
        new BuiltinBug(this, "Return of address to stack-allocated memory"));

  // Generate a report for this bug.
  SmallString<512> buf;
  llvm::raw_svector_ostream os(buf);
  SourceRange range = genName(os, R, C.getASTContext());
  os << " returned to caller";
  BugReport *report = new BugReport(*BT_returnstack, os.str(), N);
  report->addRange(RetE->getSourceRange());
  if (range.isValid())
    report->addRange(range);

  C.emitReport(report);
}

void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
                                          CheckerContext &C) const {
  
  const Expr *RetE = RS->getRetValue();
  if (!RetE)
    return;
  RetE = RetE->IgnoreParens();

  const LocationContext *LCtx = C.getLocationContext();
  SVal V = C.getState()->getSVal(RetE, LCtx);
  const MemRegion *R = V.getAsRegion();

  if (!R)
    return;
  
  const StackSpaceRegion *SS =
    dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
    
  if (!SS)
    return;

  // Return stack memory in an ancestor stack frame is fine.
  const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame();
  const StackFrameContext *MemFrame = SS->getStackFrame();
  if (MemFrame != CurFrame)
    return;

  // Automatic reference counting automatically copies blocks.
  if (C.getASTContext().getLangOpts().ObjCAutoRefCount &&
      isa<BlockDataRegion>(R))
    return;

  // 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;

  EmitStackError(C, R, RetE);
}

void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
  ProgramStateRef state = Ctx.getState();

  // Iterate over all bindings to global variables and see if it contains
  // a memory region in the stack space.
  class CallBack : public StoreManager::BindingsHandler {
  private:
    CheckerContext &Ctx;
    const StackFrameContext *CurSFC;
  public:
    SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;

    CallBack(CheckerContext &CC) :
      Ctx(CC),
      CurSFC(CC.getLocationContext()->getCurrentStackFrame())
    {}
    
    bool HandleBinding(StoreManager &SMgr, Store store,
                       const MemRegion *region, SVal val) {
      
      if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
        return true;
      
      const MemRegion *vR = val.getAsRegion();
      if (!vR)
        return true;
        
      // Under automated retain release, it is okay to assign a block
      // directly to a global variable.
      if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
          isa<BlockDataRegion>(vR))
        return true;

      if (const StackSpaceRegion *SSR = 
          dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
        // If the global variable holds a location in the current stack frame,
        // record the binding to emit a warning.
        if (SSR->getStackFrame() == CurSFC)
          V.push_back(std::make_pair(region, vR));
      }
      
      return true;
    }
  };
    
  CallBack cb(Ctx);
  state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);

  if (cb.V.empty())
    return;

  // Generate an error node.
  ExplodedNode *N = Ctx.addTransition(state);
  if (!N)
    return;

  if (!BT_stackleak)
    BT_stackleak.reset(
        new BuiltinBug(this, "Stack address stored into global variable",
                       "Stack address was saved into a global variable. "
                       "This is dangerous because the address will become "
                       "invalid after returning from the function"));

  for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
    // Generate a report for this bug.
    SmallString<512> buf;
    llvm::raw_svector_ostream os(buf);
    SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext());
    os << " is still referred to by the global variable '";
    const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
    os << *VR->getDecl()
       << "' upon returning to the caller.  This will be a dangling reference";
    BugReport *report = new BugReport(*BT_stackleak, os.str(), N);
    if (range.isValid())
      report->addRange(range);

    Ctx.emitReport(report);
  }
}

void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) {
  mgr.registerChecker<StackAddrEscapeChecker>();
}
@


