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