32enum ChrootKind { NO_CHROOT, ROOT_CHANGED, ROOT_CHANGE_FAILED, JAIL_ENTERED };
51class ChrootChecker final :
public Checker<eval::Call, check::PreCall> {
53 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
54 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
57 bool evalChroot(
const CallEvent &
Call, CheckerContext &
C)
const;
58 bool evalChdir(
const CallEvent &
Call, CheckerContext &
C)
const;
60 const BugType BreakJailBug{
this,
"Break out of jail"};
61 const CallDescription Chroot{CDM::CLibrary, {
"chroot"}, 1};
62 const CallDescription Chdir{CDM::CLibrary, {
"chdir"}, 1};
67 return evalChroot(
Call,
C);
70 return evalChdir(
Call,
C);
75bool ChrootChecker::evalChroot(
const CallEvent &
Call, CheckerContext &
C)
const {
76 BasicValueFactory &BVF =
C.getSValBuilder().getBasicValueFactory();
77 const LocationContext *LCtx =
C.getLocationContext();
81 const QualType IntTy =
C.getASTContext().IntTy;
82 SVal
Zero = nonloc::ConcreteInt{BVF.getValue(0, IntTy)};
83 SVal Minus1 = nonloc::ConcreteInt{BVF.getValue(-1, IntTy)};
86 C.addTransition(ChrootFailed->set<ChrootState>(ROOT_CHANGE_FAILED));
89 C.addTransition(ChrootSucceeded->set<ChrootState>(ROOT_CHANGED));
93bool ChrootChecker::evalChdir(
const CallEvent &
Call, CheckerContext &
C)
const {
97 if (State->get<ChrootState>() == NO_CHROOT)
101 SVal ArgVal =
Call.getArgSVal(0);
105 if (
const auto *StrRegion = dyn_cast<StringRegion>(R)) {
106 if (StrRegion->getStringLiteral()->getString() ==
"/") {
107 C.addTransition(State->set<ChrootState>(JAIL_ENTERED));
115class ChrootInvocationVisitor final :
public BugReporterVisitor {
117 explicit ChrootInvocationVisitor(
const CallDescription &Chroot)
121 BugReporterContext &BRC,
122 PathSensitiveBugReport &BR)
override {
130 const CallExpr *
Call = StmtP->getStmtAs<CallExpr>();
140 return std::make_shared<PathDiagnosticEventPiece>(Pos,
"chroot called here",
144 void Profile(llvm::FoldingSetNodeID &ID)
const override {
150 const CallDescription &Chroot;
151 bool Satisfied =
false;
155void ChrootChecker::checkPreCall(
const CallEvent &
Call,
156 CheckerContext &
C)
const {
158 if (matchesAny(
Call, Chroot, Chdir))
162 if (
C.getState()->get<ChrootState>() != ROOT_CHANGED)
167 C.generateNonFatalErrorNode(
C.getState(),
C.getPredecessor());
171 auto R = std::make_unique<PathSensitiveBugReport>(
172 BreakJailBug, R
"(No call of chdir("/") immediately after chroot)", Err);
173 R->addVisitor<ChrootInvocationVisitor>(Chroot);
174 C.emitReport(std::move(R));
179void ento::registerChrootChecker(CheckerManager &Mgr) {
183bool ento::shouldRegisterChrootChecker(
const CheckerManager &) {
return true; }
Defines the clang::ASTContext interface.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
std::optional< T > getAs() const
Convert to the specified ProgramPoint type, returning std::nullopt if this ProgramPoint is not of the...
const SourceManager & getSourceManager() const
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
bool matchesAsWritten(const CallExpr &CE) const
Returns true if the CallExpr is a call to a function that matches the CallDescription.
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
const LocationContext * getLocationContext() const
const MemRegion * getAsRegion() const
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
U cast(CodeGen::Address addr)