#include "ErrnoModeling.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
using namespace ento;
namespace {
const char *ErrnoVarName = "errno";
const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
"__errno", "_errno", "__error"};
class ErrnoModeling
: public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
check::LiveSymbols, eval::Call> {
public:
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
BugReporter &BR) const;
void checkBeginFunction(CheckerContext &C) const;
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
mutable const Decl *ErrnoDecl = nullptr;
private:
CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0},
{"___errno", 0, 0},
{"__errno", 0, 0},
{"_errno", 0, 0},
{"__error", 0, 0}};
};
}
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
static const VarDecl *getErrnoVar(ASTContext &ACtx) {
IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
if (auto *VD = dyn_cast<VarDecl>(D))
return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
VD->hasExternalStorage() &&
VD->getType().getCanonicalType() == ACtx.IntTy;
return false;
});
if (Found == LookupRes.end())
return nullptr;
return cast<VarDecl>(*Found);
}
static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
SmallVector<const Decl *> LookupRes;
for (StringRef ErrnoName : ErrnoLocationFuncNames) {
IdentifierInfo &II = ACtx.Idents.get(ErrnoName);
llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II));
}
auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
if (auto *FD = dyn_cast<FunctionDecl>(D))
return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) &&
FD->isExternC() && FD->getNumParams() == 0 &&
FD->getReturnType().getCanonicalType() ==
ACtx.getPointerType(ACtx.IntTy);
return false;
});
if (Found == LookupRes.end())
return nullptr;
return cast<FunctionDecl>(*Found);
}
void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
AnalysisManager &Mgr, BugReporter &BR) const {
ErrnoDecl = getErrnoVar(Mgr.getASTContext());
if (!ErrnoDecl)
ErrnoDecl = getErrnoFunc(Mgr.getASTContext());
}
void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
if (!C.inTopFrame())
return;
ASTContext &ACtx = C.getASTContext();
ProgramStateRef State = C.getState();
if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
const MemRegion *ErrnoR =
State->getRegion(ErrnoVar, C.getLocationContext());
assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
State = State->set<ErrnoRegion>(ErrnoR);
State =
errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
C.addTransition(State);
} else if (ErrnoDecl) {
assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
SValBuilder &SVB = C.getSValBuilder();
MemRegionManager &RMgr = C.getStateManager().getRegionManager();
const MemSpaceRegion *GlobalSystemSpace =
RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
const SymbolConjured *Sym = SVB.conjureSymbol(
nullptr, C.getLocationContext(),
ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
const MemRegion *ErrnoR = RMgr.getElementRegion(
ACtx.IntTy, SVB.makeZeroArrayIndex(),
RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
State = State->set<ErrnoRegion>(ErrnoR);
State =
errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
C.addTransition(State);
}
}
bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
if (ErrnoLocationCalls.contains(Call)) {
ProgramStateRef State = C.getState();
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return false;
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
loc::MemRegionVal{ErrnoR});
C.addTransition(State);
return true;
}
return false;
}
void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
SymbolReaper &SR) const {
if (const auto *ErrnoR = State->get<ErrnoRegion>())
SR.markLive(ErrnoR);
}
namespace clang {
namespace ento {
namespace errno_modeling {
Optional<SVal> getErrnoValue(ProgramStateRef State) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return {};
QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
return State->getSVal(ErrnoR, IntTy);
}
ProgramStateRef setErrnoValue(ProgramStateRef State,
const LocationContext *LCtx, SVal Value,
ErrnoCheckState EState) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return State;
State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
return State->set<ErrnoState>(EState);
}
ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
uint64_t Value, ErrnoCheckState EState) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return State;
State = State->bindLoc(
loc::MemRegionVal{ErrnoR},
C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
C.getLocationContext());
return State->set<ErrnoState>(EState);
}
Optional<Loc> getErrnoLoc(ProgramStateRef State) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return {};
return loc::MemRegionVal{ErrnoR};
}
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
return State->set<ErrnoState>(EState);
}
ErrnoCheckState getErrnoState(ProgramStateRef State) {
return State->get<ErrnoState>();
}
bool isErrno(const Decl *D) {
if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
if (const IdentifierInfo *II = VD->getIdentifier())
return II->getName() == ErrnoVarName;
if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
if (const IdentifierInfo *II = FD->getIdentifier())
return llvm::is_contained(ErrnoLocationFuncNames, II->getName());
return false;
}
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
if (ErrnoR && BR.isInteresting(ErrnoR)) {
BR.markNotInteresting(ErrnoR);
return Message;
}
return "";
});
}
} } }
void ento::registerErrnoModeling(CheckerManager &mgr) {
mgr.registerChecker<ErrnoModeling>();
}
bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
return true;
}