/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ChrootChecker.cpp - chroot usage checks ---------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines chroot checker, which checks improper use of chroot. |
10 | | // |
11 | | //===----------------------------------------------------------------------===// |
12 | | |
13 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
14 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
15 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
16 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
17 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
18 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
19 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
21 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
22 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
23 | | |
24 | | using namespace clang; |
25 | | using namespace ento; |
26 | | |
27 | | namespace { |
28 | | |
29 | | // enum value that represent the jail state |
30 | | enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED }; |
31 | | |
32 | 3 | bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } |
33 | | //bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; } |
34 | | |
35 | | // This checker checks improper use of chroot. |
36 | | // The state transition: |
37 | | // NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED |
38 | | // | | |
39 | | // ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)-- |
40 | | // | | |
41 | | // bug<--foo()-- JAIL_ENTERED<--foo()-- |
42 | | class ChrootChecker : public Checker<eval::Call, check::PreCall> { |
43 | | // This bug refers to possibly break out of a chroot() jail. |
44 | | mutable std::unique_ptr<BugType> BT_BreakJail; |
45 | | |
46 | | const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1}; |
47 | | |
48 | | public: |
49 | 4 | ChrootChecker() {} |
50 | | |
51 | 34 | static void *getTag() { |
52 | 34 | static int x; |
53 | 34 | return &x; |
54 | 34 | } |
55 | | |
56 | | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
57 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
58 | | |
59 | | private: |
60 | | void evalChroot(const CallEvent &Call, CheckerContext &C) const; |
61 | | void evalChdir(const CallEvent &Call, CheckerContext &C) const; |
62 | | }; |
63 | | |
64 | | } // end anonymous namespace |
65 | | |
66 | 33 | bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { |
67 | 33 | if (Chroot.matches(Call)) { |
68 | 3 | evalChroot(Call, C); |
69 | 3 | return true; |
70 | 3 | } |
71 | 30 | if (Chdir.matches(Call)) { |
72 | 2 | evalChdir(Call, C); |
73 | 2 | return true; |
74 | 2 | } |
75 | | |
76 | 28 | return false; |
77 | 30 | } |
78 | | |
79 | 3 | void ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const { |
80 | 3 | ProgramStateRef state = C.getState(); |
81 | 3 | ProgramStateManager &Mgr = state->getStateManager(); |
82 | | |
83 | | // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in |
84 | | // the GDM. |
85 | 3 | state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED); |
86 | 3 | C.addTransition(state); |
87 | 3 | } |
88 | | |
89 | 2 | void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const { |
90 | 2 | ProgramStateRef state = C.getState(); |
91 | 2 | ProgramStateManager &Mgr = state->getStateManager(); |
92 | | |
93 | | // If there are no jail state in the GDM, just return. |
94 | 2 | const void *k = state->FindGDM(ChrootChecker::getTag()); |
95 | 2 | if (!k) |
96 | 0 | return; |
97 | | |
98 | | // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. |
99 | 2 | const Expr *ArgExpr = Call.getArgExpr(0); |
100 | 2 | SVal ArgVal = C.getSVal(ArgExpr); |
101 | | |
102 | 2 | if (const MemRegion *R = ArgVal.getAsRegion()) { |
103 | 2 | R = R->StripCasts(); |
104 | 2 | if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) { |
105 | 2 | const StringLiteral* Str = StrRegion->getStringLiteral(); |
106 | 2 | if (Str->getString() == "/") |
107 | 1 | state = Mgr.addGDM(state, ChrootChecker::getTag(), |
108 | 1 | (void*) JAIL_ENTERED); |
109 | 2 | } |
110 | 2 | } |
111 | | |
112 | 2 | C.addTransition(state); |
113 | 2 | } |
114 | | |
115 | | // Check the jail state before any function call except chroot and chdir(). |
116 | | void ChrootChecker::checkPreCall(const CallEvent &Call, |
117 | 33 | CheckerContext &C) const { |
118 | | // Ignore chroot and chdir. |
119 | 33 | if (matchesAny(Call, Chroot, Chdir)) |
120 | 5 | return; |
121 | | |
122 | | // If jail state is ROOT_CHANGED, generate BugReport. |
123 | 28 | void *const* k = C.getState()->FindGDM(ChrootChecker::getTag()); |
124 | 28 | if (k) |
125 | 3 | if (isRootChanged((intptr_t) *k)) |
126 | 2 | if (ExplodedNode *N = C.generateNonFatalErrorNode()) { |
127 | 2 | if (!BT_BreakJail) |
128 | 1 | BT_BreakJail.reset(new BugType(this, "Break out of jail")); |
129 | 2 | constexpr llvm::StringLiteral Msg = |
130 | 2 | "No call of chdir(\"/\") immediately after chroot"; |
131 | 2 | C.emitReport( |
132 | 2 | std::make_unique<PathSensitiveBugReport>(*BT_BreakJail, Msg, N)); |
133 | 2 | } |
134 | 28 | } |
135 | | |
136 | 4 | void ento::registerChrootChecker(CheckerManager &mgr) { |
137 | 4 | mgr.registerChecker<ChrootChecker>(); |
138 | 4 | } |
139 | | |
140 | 8 | bool ento::shouldRegisterChrootChecker(const CheckerManager &mgr) { |
141 | 8 | return true; |
142 | 8 | } |