1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
//=== StackAddrLeakChecker.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 "GRExprEngineInternalChecks.h"
#include "clang/Checker/BugReporter/BugType.h"
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
#include "clang/Checker/PathSensitive/GRState.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallString.h"
using namespace clang;
namespace {
class StackAddrLeakChecker : public CheckerVisitor<StackAddrLeakChecker> {
BuiltinBug *BT_stackleak;
BuiltinBug *BT_returnstack;
public:
StackAddrLeakChecker() : BT_stackleak(0), BT_returnstack(0) {}
static void *getTag() {
static int x;
return &x;
}
void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS);
void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng);
private:
void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE);
};
}
void clang::RegisterStackAddrLeakChecker(GRExprEngine &Eng) {
Eng.registerCheck(new StackAddrLeakChecker());
}
void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
const Expr *RetE) {
ExplodedNode *N = C.GenerateSink();
if (!N)
return;
if (!BT_returnstack)
BT_returnstack=new BuiltinBug("Return of address to stack-allocated memory");
// Generate a report for this bug.
llvm::SmallString<512> buf;
llvm::raw_svector_ostream os(buf);
SourceRange range;
// Get the base region, stripping away fields and elements.
R = R->getBaseRegion();
// Check if the region is a compound literal.
if (const CompoundLiteralRegion* CR = dyn_cast<CompoundLiteralRegion>(R)) {
const CompoundLiteralExpr* CL = CR->getLiteralExpr();
os << "Address of stack memory associated with a compound literal "
"declared on line "
<< C.getSourceManager().getInstantiationLineNumber(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 << "Address of stack memory allocated by call to alloca() on line "
<< C.getSourceManager().getInstantiationLineNumber(L)
<< " returned to caller";
}
else if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
const BlockDecl *BD = BR->getCodeRegion()->getDecl();
SourceLocation L = BD->getLocStart();
range = BD->getSourceRange();
os << "Address of stack-allocated block declared on line "
<< C.getSourceManager().getInstantiationLineNumber(L)
<< " returned to caller";
}
else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << "Address of stack memory associated with local variable '"
<< VR->getString() << "' returned";
range = VR->getDecl()->getSourceRange();
}
else {
assert(false && "Invalid region in ReturnStackAddressChecker.");
return;
}
RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N);
report->addRange(RetE->getSourceRange());
if (range.isValid())
report->addRange(range);
C.EmitReport(report);
}
void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C,
const ReturnStmt *RS) {
const Expr *RetE = RS->getRetValue();
if (!RetE)
return;
SVal V = C.getState()->getSVal(RetE);
const MemRegion *R = V.getAsRegion();
if (!R || !R->hasStackStorage())
return;
if (R->hasStackStorage()) {
EmitStackError(C, R, RetE);
return;
}
}
void StackAddrLeakChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag,
GRExprEngine &Eng) {
SaveAndRestore<bool> OldHasGen(B.HasGeneratedNode);
const GRState *state = B.getState();
TranslationUnitDecl *TU = Eng.getContext().getTranslationUnitDecl();
// Check each global variable if it contains a MemRegionVal of a stack
// variable declared in the function we are leaving.
for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end();
I != E; ++I) {
if (VarDecl *VD = dyn_cast<VarDecl>(*I)) {
const LocationContext *LCtx = B.getPredecessor()->getLocationContext();
SVal L = state->getLValue(VD, LCtx);
SVal V = state->getSVal(cast<Loc>(L));
if (loc::MemRegionVal *RV = dyn_cast<loc::MemRegionVal>(&V)) {
const MemRegion *R = RV->getRegion();
if (const StackSpaceRegion *SSR =
dyn_cast<StackSpaceRegion>(R->getMemorySpace())) {
const StackFrameContext *ValSFC = SSR->getStackFrame();
const StackFrameContext *CurSFC = LCtx->getCurrentStackFrame();
// If the global variable holds a location in the current stack frame,
// emit a warning.
if (ValSFC == CurSFC) {
// The variable is declared in the function scope which we are
// leaving. Keeping this variable's address in a global variable
// is dangerous.
// FIXME: better warning location.
ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
if (N) {
if (!BT_stackleak)
BT_stackleak = new BuiltinBug("Stack address leak",
"Stack address was saved into a global variable. "
"is dangerous because the address will become invalid "
"after returning from the function.");
BugReport *R = new BugReport(*BT_stackleak,
BT_stackleak->getDescription(), N);
Eng.getBugReporter().EmitReport(R);
}
}
}
}
}
}
}
|