xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //===- Chrootchecker.cpp -------- Basic security checks ---------*- C++ -*-===//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li //  This file defines chroot checker, which checks improper use of chroot.
11*67e74705SXin Li //
12*67e74705SXin Li //===----------------------------------------------------------------------===//
13*67e74705SXin Li 
14*67e74705SXin Li #include "ClangSACheckers.h"
15*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
17*67e74705SXin Li #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22*67e74705SXin Li #include "llvm/ADT/ImmutableMap.h"
23*67e74705SXin Li 
24*67e74705SXin Li using namespace clang;
25*67e74705SXin Li using namespace ento;
26*67e74705SXin Li 
27*67e74705SXin Li namespace {
28*67e74705SXin Li 
29*67e74705SXin Li // enum value that represent the jail state
30*67e74705SXin Li enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };
31*67e74705SXin Li 
isRootChanged(intptr_t k)32*67e74705SXin Li bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
33*67e74705SXin Li //bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
34*67e74705SXin Li 
35*67e74705SXin Li // This checker checks improper use of chroot.
36*67e74705SXin Li // The state transition:
37*67e74705SXin Li // NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED
38*67e74705SXin Li //                                  |                               |
39*67e74705SXin Li //         ROOT_CHANGED<--chdir(..)--      JAIL_ENTERED<--chdir(..)--
40*67e74705SXin Li //                                  |                               |
41*67e74705SXin Li //                      bug<--foo()--          JAIL_ENTERED<--foo()--
42*67e74705SXin Li class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > {
43*67e74705SXin Li   mutable IdentifierInfo *II_chroot, *II_chdir;
44*67e74705SXin Li   // This bug refers to possibly break out of a chroot() jail.
45*67e74705SXin Li   mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
46*67e74705SXin Li 
47*67e74705SXin Li public:
ChrootChecker()48*67e74705SXin Li   ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {}
49*67e74705SXin Li 
getTag()50*67e74705SXin Li   static void *getTag() {
51*67e74705SXin Li     static int x;
52*67e74705SXin Li     return &x;
53*67e74705SXin Li   }
54*67e74705SXin Li 
55*67e74705SXin Li   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
56*67e74705SXin Li   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
57*67e74705SXin Li 
58*67e74705SXin Li private:
59*67e74705SXin Li   void Chroot(CheckerContext &C, const CallExpr *CE) const;
60*67e74705SXin Li   void Chdir(CheckerContext &C, const CallExpr *CE) const;
61*67e74705SXin Li };
62*67e74705SXin Li 
63*67e74705SXin Li } // end anonymous namespace
64*67e74705SXin Li 
evalCall(const CallExpr * CE,CheckerContext & C) const65*67e74705SXin Li bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
66*67e74705SXin Li   const FunctionDecl *FD = C.getCalleeDecl(CE);
67*67e74705SXin Li   if (!FD)
68*67e74705SXin Li     return false;
69*67e74705SXin Li 
70*67e74705SXin Li   ASTContext &Ctx = C.getASTContext();
71*67e74705SXin Li   if (!II_chroot)
72*67e74705SXin Li     II_chroot = &Ctx.Idents.get("chroot");
73*67e74705SXin Li   if (!II_chdir)
74*67e74705SXin Li     II_chdir = &Ctx.Idents.get("chdir");
75*67e74705SXin Li 
76*67e74705SXin Li   if (FD->getIdentifier() == II_chroot) {
77*67e74705SXin Li     Chroot(C, CE);
78*67e74705SXin Li     return true;
79*67e74705SXin Li   }
80*67e74705SXin Li   if (FD->getIdentifier() == II_chdir) {
81*67e74705SXin Li     Chdir(C, CE);
82*67e74705SXin Li     return true;
83*67e74705SXin Li   }
84*67e74705SXin Li 
85*67e74705SXin Li   return false;
86*67e74705SXin Li }
87*67e74705SXin Li 
Chroot(CheckerContext & C,const CallExpr * CE) const88*67e74705SXin Li void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
89*67e74705SXin Li   ProgramStateRef state = C.getState();
90*67e74705SXin Li   ProgramStateManager &Mgr = state->getStateManager();
91*67e74705SXin Li 
92*67e74705SXin Li   // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
93*67e74705SXin Li   // the GDM.
94*67e74705SXin Li   state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
95*67e74705SXin Li   C.addTransition(state);
96*67e74705SXin Li }
97*67e74705SXin Li 
Chdir(CheckerContext & C,const CallExpr * CE) const98*67e74705SXin Li void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
99*67e74705SXin Li   ProgramStateRef state = C.getState();
100*67e74705SXin Li   ProgramStateManager &Mgr = state->getStateManager();
101*67e74705SXin Li 
102*67e74705SXin Li   // If there are no jail state in the GDM, just return.
103*67e74705SXin Li   const void *k = state->FindGDM(ChrootChecker::getTag());
104*67e74705SXin Li   if (!k)
105*67e74705SXin Li     return;
106*67e74705SXin Li 
107*67e74705SXin Li   // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
108*67e74705SXin Li   const Expr *ArgExpr = CE->getArg(0);
109*67e74705SXin Li   SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
110*67e74705SXin Li 
111*67e74705SXin Li   if (const MemRegion *R = ArgVal.getAsRegion()) {
112*67e74705SXin Li     R = R->StripCasts();
113*67e74705SXin Li     if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
114*67e74705SXin Li       const StringLiteral* Str = StrRegion->getStringLiteral();
115*67e74705SXin Li       if (Str->getString() == "/")
116*67e74705SXin Li         state = Mgr.addGDM(state, ChrootChecker::getTag(),
117*67e74705SXin Li                            (void*) JAIL_ENTERED);
118*67e74705SXin Li     }
119*67e74705SXin Li   }
120*67e74705SXin Li 
121*67e74705SXin Li   C.addTransition(state);
122*67e74705SXin Li }
123*67e74705SXin Li 
124*67e74705SXin Li // Check the jail state before any function call except chroot and chdir().
checkPreStmt(const CallExpr * CE,CheckerContext & C) const125*67e74705SXin Li void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
126*67e74705SXin Li   const FunctionDecl *FD = C.getCalleeDecl(CE);
127*67e74705SXin Li   if (!FD)
128*67e74705SXin Li     return;
129*67e74705SXin Li 
130*67e74705SXin Li   ASTContext &Ctx = C.getASTContext();
131*67e74705SXin Li   if (!II_chroot)
132*67e74705SXin Li     II_chroot = &Ctx.Idents.get("chroot");
133*67e74705SXin Li   if (!II_chdir)
134*67e74705SXin Li     II_chdir = &Ctx.Idents.get("chdir");
135*67e74705SXin Li 
136*67e74705SXin Li   // Ingnore chroot and chdir.
137*67e74705SXin Li   if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
138*67e74705SXin Li     return;
139*67e74705SXin Li 
140*67e74705SXin Li   // If jail state is ROOT_CHANGED, generate BugReport.
141*67e74705SXin Li   void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
142*67e74705SXin Li   if (k)
143*67e74705SXin Li     if (isRootChanged((intptr_t) *k))
144*67e74705SXin Li       if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
145*67e74705SXin Li         if (!BT_BreakJail)
146*67e74705SXin Li           BT_BreakJail.reset(new BuiltinBug(
147*67e74705SXin Li               this, "Break out of jail", "No call of chdir(\"/\") immediately "
148*67e74705SXin Li                                          "after chroot"));
149*67e74705SXin Li         C.emitReport(llvm::make_unique<BugReport>(
150*67e74705SXin Li             *BT_BreakJail, BT_BreakJail->getDescription(), N));
151*67e74705SXin Li       }
152*67e74705SXin Li }
153*67e74705SXin Li 
registerChrootChecker(CheckerManager & mgr)154*67e74705SXin Li void ento::registerChrootChecker(CheckerManager &mgr) {
155*67e74705SXin Li   mgr.registerChecker<ChrootChecker>();
156*67e74705SXin Li }
157