llvm-project/clang/lib/Checker/StackAddrLeakChecker.cpp
Ted Kremenek 5df037e808 Tweak stack address checker to report multiple cases where globals may reference stack memory.
Also refactor the diagnostics so that we print out the kind of stack memory returned.

llvm-svn: 106210
2010-06-17 04:21:37 +00:00

205 lines
6.9 KiB
C++

//=== 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);
SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R,
SourceManager &SM);
};
}
void clang::RegisterStackAddrLeakChecker(GRExprEngine &Eng) {
Eng.registerCheck(new StackAddrLeakChecker());
}
SourceRange StackAddrLeakChecker::GenName(llvm::raw_ostream &os,
const MemRegion *R,
SourceManager &SM) {
// Get the base region, stripping away fields and elements.
R = R->getBaseRegion();
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.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 << "stack memory allocated by call to alloca() on line "
<< SM.getInstantiationLineNumber(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.getInstantiationLineNumber(L);
}
else if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
os << "stack memory associated with local variable '"
<< VR->getString() << '\'';
range = VR->getDecl()->getSourceRange();
}
else {
assert(false && "Invalid region in ReturnStackAddressChecker.");
}
return range;
}
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 = GenName(os, R, C.getSourceManager());
os << " returned to caller";
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();
// 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:
const StackFrameContext *CurSFC;
public:
llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V;
CallBack(const LocationContext *LCtx)
: CurSFC(LCtx->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;
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(B.getPredecessor()->getLocationContext());
state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb);
if (cb.V.empty())
return;
// Generate an error node.
ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor());
if (!N)
return;
if (!BT_stackleak)
BT_stackleak =
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.
llvm::SmallString<512> buf;
llvm::raw_svector_ostream os(buf);
SourceRange range = GenName(os, cb.V[i].second,
Eng.getContext().getSourceManager());
os << " is still referred to by the global variable '";
const VarRegion *VR = cast<VarRegion>(cb.V[i].first->getBaseRegion());
os << VR->getDecl()->getNameAsString()
<< "' upon returning to the caller. This will be a dangling reference";
RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N);
if (range.isValid())
report->addRange(range);
Eng.getBugReporter().EmitReport(report);
}
}