blob: f10da977172c0769eadaaddb2a8e9ee2350929f1 [file] [edit]
#include "LifetimeModeling.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
using namespace clang;
using namespace ento;
namespace {
class UseAfterLifetimeEnd : public Checker<check::EndFunction> {
public:
void reportDanglingSource(const MemRegion *Source, SVal Val, ExplodedNode *N,
CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
const BugType BugMsg{this, "UseAfterLifetimeEnd", "LifetimeBound"};
};
class UseAfterLifetimeEndBRVisitor : public BugReporterVisitor {
SVal BoundVal;
const MemRegion *SourceRegion;
public:
explicit UseAfterLifetimeEndBRVisitor(SVal Val, const MemRegion *Source)
: BoundVal(Val), SourceRegion(Source) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int X = 0;
ID.AddPointer(&X);
BoundVal.Profile(ID);
SourceRegion->Profile(ID);
}
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
PathDiagnosticPieceRef getEndPath(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
PathDiagnosticPieceRef createSourcePiece(const ExplodedNode *N,
BugReporterContext &BRC,
StringRef Message) const;
};
} // namespace
static const Expr *getLifetimeBoundArg(const Expr *RetExpr,
const MemRegion *Region,
const ExplodedNode *N) {
const CallExpr *Expr = dyn_cast_or_null<CallExpr>(RetExpr);
if (!Expr)
return nullptr;
const FunctionDecl *FD = Expr->getDirectCallee();
if (!FD)
return nullptr;
const MemRegion *BaseReg = Region->getBaseRegion();
for (const ParmVarDecl *PVD : FD->parameters()) {
if (!PVD->hasAttr<LifetimeBoundAttr>())
continue;
unsigned Idx = PVD->getFunctionScopeIndex();
if (Idx >= Expr->getNumArgs())
continue;
const MemRegion *R = N->getSVal(Expr->getArg(Idx)).getAsRegion();
if (R && R->getBaseRegion() == BaseReg)
return Expr->getArg(Idx);
}
return nullptr;
}
void UseAfterLifetimeEnd::checkEndFunction(const ReturnStmt *RS,
CheckerContext &C) const {
if (!RS)
return;
ProgramStateRef State = C.getState();
const Expr *RetExpr = RS->getRetValue();
if (!RetExpr)
return;
RetExpr = RetExpr->IgnoreParens();
SVal RetVal = C.getSVal(RetExpr);
std::vector<const MemRegion *> RetValRegion =
lifetime_modeling::getDanglingRegionsAfterReturn(RetVal, State, C);
if (RetValRegion.empty())
return;
if (ExplodedNode *N =
C.generateNonFatalErrorNode(State, C.getPredecessor())) {
for (const MemRegion *R : RetValRegion)
reportDanglingSource(R, RetVal, N, C);
}
}
static SourceRange getRegionDeclRange(const MemRegion *Source) {
if (const auto *VR = dyn_cast_or_null<VarRegion>(Source)) {
const VarDecl *VD = VR->getDecl();
return SourceRange(VD->getLocation());
}
return SourceRange();
}
void UseAfterLifetimeEnd::reportDanglingSource(const MemRegion *Source,
SVal RetVal, ExplodedNode *N,
CheckerContext &C) const {
auto BR = std::make_unique<PathSensitiveBugReport>(
BugMsg,
(llvm::Twine("Returning value bound to ") +
lifetime_modeling::getRegionName(Source) + " that will go out of scope"),
N);
if (SourceRange Range = getRegionDeclRange(Source); Range.isValid())
BR->addRange(Range);
BR->addVisitor<UseAfterLifetimeEndBRVisitor>(RetVal, Source);
bugreporter::trackStoredValue(RetVal, Source, *BR);
C.emitReport(std::move(BR));
}
PathDiagnosticPieceRef UseAfterLifetimeEndBRVisitor::createSourcePiece(
const ExplodedNode *N, BugReporterContext &BRC, StringRef Message) const {
const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
const Expr *RetExpr = dyn_cast_or_null<Expr>(S);
const Expr *Arg = getLifetimeBoundArg(RetExpr, SourceRegion, N);
PathDiagnosticLocation Pos;
Pos = PathDiagnosticLocation(Arg ? Arg : S, BRC.getSourceManager(),
N->getStackFrame());
auto Note = std::make_shared<PathDiagnosticEventPiece>(Pos, Message, true);
if (SourceRange Range = getRegionDeclRange(SourceRegion); Range.isValid())
Note->addRange(Range);
return Note;
}
PathDiagnosticPieceRef
UseAfterLifetimeEndBRVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
const ExplodedNode *Pred = N->getFirstPred();
if (!Pred)
return nullptr;
if (!lifetime_modeling::isBoundToLifetimeSource(N->getState(), BoundVal) ||
lifetime_modeling::isBoundToLifetimeSource(Pred->getState(), BoundVal))
return nullptr;
auto Piece = createSourcePiece(
N, BRC,
(llvm::Twine("Value's lifetime bound to the lifetime of ") +
lifetime_modeling::getRegionName(SourceRegion) + " here")
.str());
return Piece;
}
PathDiagnosticPieceRef
UseAfterLifetimeEndBRVisitor::getEndPath(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
auto Piece = createSourcePiece(
N, BRC,
(llvm::Twine("Lifetime of ") +
lifetime_modeling::getRegionName(SourceRegion) + " ended here")
.str());
return Piece;
}
void ento::registerUseAfterLifetimeEnd(CheckerManager &Mgr) {
Mgr.registerChecker<UseAfterLifetimeEnd>();
}
bool ento::shouldRegisterUseAfterLifetimeEnd(const CheckerManager &Mgr) {
return true;
}