clang  3.7.0
StackAddrEscapeChecker.cpp
Go to the documentation of this file.
1 //=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines stack address leak checker, which checks if an invalid
11 // stack address is stored into a global or heap location. See CERT DCL30-C.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ClangSACheckers.h"
16 #include "clang/AST/ExprCXX.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/Support/raw_ostream.h"
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
30  check::EndFunction > {
31  mutable std::unique_ptr<BuiltinBug> BT_stackleak;
32  mutable std::unique_ptr<BuiltinBug> BT_returnstack;
33 
34 public:
35  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
36  void checkEndFunction(CheckerContext &Ctx) const;
37 private:
38  void EmitStackError(CheckerContext &C, const MemRegion *R,
39  const Expr *RetE) const;
40  static SourceRange genName(raw_ostream &os, const MemRegion *R,
41  ASTContext &Ctx);
42 };
43 }
44 
45 SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
46  ASTContext &Ctx) {
47  // Get the base region, stripping away fields and elements.
48  R = R->getBaseRegion();
50  SourceRange range;
51  os << "Address of ";
52 
53  // Check if the region is a compound literal.
54  if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
55  const CompoundLiteralExpr *CL = CR->getLiteralExpr();
56  os << "stack memory associated with a compound literal "
57  "declared on line "
59  << " returned to caller";
60  range = CL->getSourceRange();
61  }
62  else if (const AllocaRegion* AR = dyn_cast<AllocaRegion>(R)) {
63  const Expr *ARE = AR->getExpr();
64  SourceLocation L = ARE->getLocStart();
65  range = ARE->getSourceRange();
66  os << "stack memory allocated by call to alloca() on line "
67  << SM.getExpansionLineNumber(L);
68  }
69  else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
70  const BlockDecl *BD = BR->getCodeRegion()->getDecl();
71  SourceLocation L = BD->getLocStart();
72  range = BD->getSourceRange();
73  os << "stack-allocated block declared on line "
74  << SM.getExpansionLineNumber(L);
75  }
76  else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
77  os << "stack memory associated with local variable '"
78  << VR->getString() << '\'';
79  range = VR->getDecl()->getSourceRange();
80  }
81  else if (const CXXTempObjectRegion *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
82  QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
83  os << "stack memory associated with temporary object of type '";
84  Ty.print(os, Ctx.getPrintingPolicy());
85  os << "'";
86  range = TOR->getExpr()->getSourceRange();
87  }
88  else {
89  llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
90  }
91 
92  return range;
93 }
94 
95 void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
96  const Expr *RetE) const {
97  ExplodedNode *N = C.generateSink();
98 
99  if (!N)
100  return;
101 
102  if (!BT_returnstack)
103  BT_returnstack.reset(
104  new BuiltinBug(this, "Return of address to stack-allocated memory"));
105 
106  // Generate a report for this bug.
107  SmallString<512> buf;
108  llvm::raw_svector_ostream os(buf);
109  SourceRange range = genName(os, R, C.getASTContext());
110  os << " returned to caller";
111  auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N);
112  report->addRange(RetE->getSourceRange());
113  if (range.isValid())
114  report->addRange(range);
115 
116  C.emitReport(std::move(report));
117 }
118 
119 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
120  CheckerContext &C) const {
121 
122  const Expr *RetE = RS->getRetValue();
123  if (!RetE)
124  return;
125  RetE = RetE->IgnoreParens();
126 
127  const LocationContext *LCtx = C.getLocationContext();
128  SVal V = C.getState()->getSVal(RetE, LCtx);
129  const MemRegion *R = V.getAsRegion();
130 
131  if (!R)
132  return;
133 
134  const StackSpaceRegion *SS =
135  dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
136 
137  if (!SS)
138  return;
139 
140  // Return stack memory in an ancestor stack frame is fine.
141  const StackFrameContext *CurFrame = LCtx->getCurrentStackFrame();
142  const StackFrameContext *MemFrame = SS->getStackFrame();
143  if (MemFrame != CurFrame)
144  return;
145 
146  // Automatic reference counting automatically copies blocks.
147  if (C.getASTContext().getLangOpts().ObjCAutoRefCount &&
148  isa<BlockDataRegion>(R))
149  return;
150 
151  // Returning a record by value is fine. (In this case, the returned
152  // expression will be a copy-constructor, possibly wrapped in an
153  // ExprWithCleanups node.)
154  if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
155  RetE = Cleanup->getSubExpr();
156  if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
157  return;
158 
159  EmitStackError(C, R, RetE);
160 }
161 
162 void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
163  ProgramStateRef state = Ctx.getState();
164 
165  // Iterate over all bindings to global variables and see if it contains
166  // a memory region in the stack space.
167  class CallBack : public StoreManager::BindingsHandler {
168  private:
169  CheckerContext &Ctx;
170  const StackFrameContext *CurSFC;
171  public:
173 
174  CallBack(CheckerContext &CC) :
175  Ctx(CC),
176  CurSFC(CC.getLocationContext()->getCurrentStackFrame())
177  {}
178 
179  bool HandleBinding(StoreManager &SMgr, Store store,
180  const MemRegion *region, SVal val) override {
181 
182  if (!isa<GlobalsSpaceRegion>(region->getMemorySpace()))
183  return true;
184 
185  const MemRegion *vR = val.getAsRegion();
186  if (!vR)
187  return true;
188 
189  // Under automated retain release, it is okay to assign a block
190  // directly to a global variable.
191  if (Ctx.getASTContext().getLangOpts().ObjCAutoRefCount &&
192  isa<BlockDataRegion>(vR))
193  return true;
194 
195  if (const StackSpaceRegion *SSR =
196  dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) {
197  // If the global variable holds a location in the current stack frame,
198  // record the binding to emit a warning.
199  if (SSR->getStackFrame() == CurSFC)
200  V.push_back(std::make_pair(region, vR));
201  }
202 
203  return true;
204  }
205  };
206 
207  CallBack cb(Ctx);
208  state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
209 
210  if (cb.V.empty())
211  return;
212 
213  // Generate an error node.
214  ExplodedNode *N = Ctx.addTransition(state);
215  if (!N)
216  return;
217 
218  if (!BT_stackleak)
219  BT_stackleak.reset(
220  new BuiltinBug(this, "Stack address stored into global variable",
221  "Stack address was saved into a global variable. "
222  "This is dangerous because the address will become "
223  "invalid after returning from the function"));
224 
225  for (unsigned i = 0, e = cb.V.size(); i != e; ++i) {
226  // Generate a report for this bug.
227  SmallString<512> buf;
228  llvm::raw_svector_ostream os(buf);
229  SourceRange range = genName(os, cb.V[i].second, Ctx.getASTContext());
230  os << " is still referred to by the global variable '";
231  const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
232  os << *VR->getDecl()
233  << "' upon returning to the caller. This will be a dangling reference";
234  auto report = llvm::make_unique<BugReport>(*BT_stackleak, os.str(), N);
235  if (range.isValid())
236  report->addRange(range);
237 
238  Ctx.emitReport(std::move(report));
239  }
240 }
241 
242 void ento::registerStackAddrEscapeChecker(CheckerManager &mgr) {
243  mgr.registerChecker<StackAddrEscapeChecker>();
244 }
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:77
Defines the SourceManager interface.
bool isRecordType() const
Definition: Type.h:5289
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph). Uses the default CheckerContex...
const void * Store
Definition: StoreRef.h:26
const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1063
Defines the clang::Expr interface and subclasses for C++ expressions.
const MemSpaceRegion * getMemorySpace() const
Definition: MemRegion.cpp:1031
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:89
const VarDecl * getDecl() const
Definition: MemRegion.h:877
const LangOptions & getLangOpts() const
Definition: ASTContext.h:533
const StackFrameContext * getStackFrame() const
Definition: MemRegion.h:371
ExplodedNode * generateSink(ProgramStateRef State=nullptr, ExplodedNode *Pred=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a sink node. Generating a sink stops exploration of the given path.
SourceManager & SM
const ProgramStateRef & getState() const
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid=nullptr) const
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine()) const
Definition: Type.h:907
const clang::PrintingPolicy & getPrintingPolicy() const
Definition: ASTContext.h:486
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
SourceLocation getLocStart() const LLVM_READONLY
Definition: DeclBase.h:365
SourceLocation getLocStart() const LLVM_READONLY
Definition: Expr.h:2634
CHECKER * registerChecker()
Used to register checkers.
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
const StackFrameContext * getCurrentStackFrame() const
bool isValid() const
QualType getType() const
Definition: Expr.h:125
const MemRegion * getAsRegion() const
Definition: SVals.cpp:135
const Expr * getRetValue() const
Definition: Stmt.cpp:1013
SourceManager & getSourceManager()
Definition: ASTContext.h:494
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:3791
A trivial tuple used to represent a source range.
This class handles loading and caching of source files into memory.
Expr * IgnoreParens() LLVM_READONLY
Definition: Expr.cpp:2408
const LocationContext * getLocationContext() const