#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
using namespace ento;
namespace {
class InvalidPtrChecker
: public Checker<check::Location, check::BeginFunction, check::PostCall> {
private:
BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
CheckerContext &C) const;
const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
{{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
{{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
{{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
{{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
{{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
};
void postPreviousReturnInvalidatingCall(const CallEvent &Call,
CheckerContext &C) const;
const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
{{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{"setlocale", 2},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{"localeconv", 0},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
};
public:
void checkBeginFunction(CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
void checkLocation(SVal l, bool isLoad, const Stmt *S,
CheckerContext &C) const;
};
}
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
const MemRegion *)
void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
CheckerContext &C) const {
StringRef FunctionName = Call.getCalleeIdentifier()->getName();
ProgramStateRef State = C.getState();
const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
if (!SymbolicEnvPtrRegion)
return;
State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
const NoteTag *Note =
C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
if (!BR.isInteresting(SymbolicEnvPtrRegion))
return;
Out << '\'' << FunctionName
<< "' call may invalidate the environment parameter of 'main'";
});
C.addTransition(State, Note);
}
void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef State = C.getState();
const NoteTag *Note = nullptr;
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
const MemRegion *PrevReg = *Reg;
State = State->add<InvalidMemoryRegions>(PrevReg);
Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
llvm::raw_ostream &Out) {
if (!BR.isInteresting(PrevReg))
return;
Out << '\'';
FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
Out << "' call may invalidate the result of the previous " << '\'';
FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
Out << '\'';
});
}
const LocationContext *LCtx = C.getLocationContext();
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
CE, LCtx, CE->getType(), C.blockCount());
State = State->BindExpr(CE, LCtx, RetVal);
const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
const MemRegion *MR =
const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
State = State->set<PreviousCallResultMap>(FD, MR);
ExplodedNode *Node = C.addTransition(State, Note);
const NoteTag *PreviousCallNote =
C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
if (!BR.isInteresting(MR))
return;
Out << '\'' << "'previous function call was here" << '\'';
});
C.addTransition(State, Node, PreviousCallNote);
}
static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
const MemRegion *Reg) {
while (Reg) {
if (State->contains<InvalidMemoryRegions>(Reg))
return Reg;
const auto *SymBase = Reg->getSymbolicBase();
if (!SymBase)
break;
const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
if (!SRV)
break;
Reg = SRV->getRegion();
if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
Reg = VarReg;
}
return nullptr;
}
void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
(this->**Handler)(Call, C);
if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
(this->**Handler)(Call, C);
if (C.wasInlined)
return;
ProgramStateRef State = C.getState();
for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
Call.getArgSVal(I).getAsRegion())) {
if (const MemRegion *InvalidatedSymbolicBase =
findInvalidatedSymbolicBase(State, SR)) {
ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
if (!ErrorNode)
return;
SmallString<256> Msg;
llvm::raw_svector_ostream Out(Msg);
Out << "use of invalidated pointer '";
Call.getArgExpr(I)->printPretty(Out, nullptr,
C.getASTContext().getPrintingPolicy());
Out << "' in a function call";
auto Report =
std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
Report->markInteresting(InvalidatedSymbolicBase);
Report->addRange(Call.getArgSourceRange(I));
C.emitReport(std::move(Report));
}
}
}
}
void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
if (!C.inTopFrame())
return;
const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
if (!FD || FD->param_size() != 3 || !FD->isMain())
return;
ProgramStateRef State = C.getState();
const MemRegion *EnvpReg =
State->getRegion(FD->parameters()[2], C.getLocationContext());
C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
}
void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
const MemRegion *InvalidatedSymbolicBase =
findInvalidatedSymbolicBase(State, Loc.getAsRegion());
if (!InvalidatedSymbolicBase)
return;
ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
if (!ErrorNode)
return;
auto Report = std::make_unique<PathSensitiveBugReport>(
BT, "dereferencing an invalid pointer", ErrorNode);
Report->markInteresting(InvalidatedSymbolicBase);
C.emitReport(std::move(Report));
}
void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
Mgr.registerChecker<InvalidPtrChecker>();
}
bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
return true;
}