Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- VforkChecker.cpp -------- Vfork usage checks --------------*- C++ -*-==//
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 vfork checker which checks for dangerous uses of vfork.
10
//  Vforked process shares memory (including stack) with parent so it's
11
//  range of actions is significantly limited: can't write variables,
12
//  can't call functions not in the allowed list, etc. For more details, see
13
//  http://man7.org/linux/man-pages/man2/vfork.2.html
14
//
15
//  This checker checks for prohibited constructs in vforked process.
16
//  The state transition diagram:
17
//  PARENT ---(vfork() == 0)--> CHILD
18
//                                   |
19
//                                   --(*p = ...)--> bug
20
//                                   |
21
//                                   --foo()--> bug
22
//                                   |
23
//                                   --return--> bug
24
//
25
//===----------------------------------------------------------------------===//
26
27
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
28
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
29
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
30
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
31
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
32
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
33
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
34
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
35
#include "clang/StaticAnalyzer/Core/Checker.h"
36
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
37
#include "clang/AST/ParentMap.h"
38
39
using namespace clang;
40
using namespace ento;
41
42
namespace {
43
44
class VforkChecker : public Checker<check::PreCall, check::PostCall,
45
                                    check::Bind, check::PreStmt<ReturnStmt>> {
46
  mutable std::unique_ptr<BuiltinBug> BT;
47
  mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkAllowlist;
48
  mutable const IdentifierInfo *II_vfork;
49
50
  static bool isChildProcess(const ProgramStateRef State);
51
52
  bool isVforkCall(const Decl *D, CheckerContext &C) const;
53
  bool isCallExplicitelyAllowed(const IdentifierInfo *II,
54
                                CheckerContext &C) const;
55
56
  void reportBug(const char *What, CheckerContext &C,
57
                 const char *Details = nullptr) const;
58
59
public:
60
65
  VforkChecker() : II_vfork(nullptr) {}
61
62
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
63
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
64
  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
65
  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
66
};
67
68
} // end anonymous namespace
69
70
// This trait holds region of variable that is assigned with vfork's
71
// return value (this is the only region child is allowed to write).
72
// VFORK_RESULT_INVALID means that we are in parent process.
73
// VFORK_RESULT_NONE means that vfork's return value hasn't been assigned.
74
// Other values point to valid regions.
75
REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *)
76
3.46k
#define VFORK_RESULT_INVALID 0
77
8
#define VFORK_RESULT_NONE ((void *)(uintptr_t)1)
78
79
3.46k
bool VforkChecker::isChildProcess(const ProgramStateRef State) {
80
3.46k
  return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID;
81
3.46k
}
82
83
1.22k
bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {
84
1.22k
  auto FD = dyn_cast_or_null<FunctionDecl>(D);
85
1.22k
  if (!FD || 
!C.isCLibraryFunction(FD)1.22k
)
86
105
    return false;
87
88
1.12k
  if (!II_vfork) {
89
30
    ASTContext &AC = C.getASTContext();
90
30
    II_vfork = &AC.Idents.get("vfork");
91
30
  }
92
93
1.12k
  return FD->getIdentifier() == II_vfork;
94
1.22k
}
95
96
// Returns true iff ok to call function after successful vfork.
97
bool VforkChecker::isCallExplicitelyAllowed(const IdentifierInfo *II,
98
44
                                            CheckerContext &C) const {
99
44
  if (VforkAllowlist.empty()) {
100
    // According to manpage.
101
2
    const char *ids[] = {
102
2
      "_Exit",
103
2
      "_exit",
104
2
      "execl",
105
2
      "execle",
106
2
      "execlp",
107
2
      "execv",
108
2
      "execve",
109
2
      "execvp",
110
2
      "execvpe",
111
2
      nullptr
112
2
    };
113
114
2
    ASTContext &AC = C.getASTContext();
115
20
    for (const char **id = ids; *id; 
++id18
)
116
18
      VforkAllowlist.insert(&AC.Idents.get(*id));
117
2
  }
118
119
44
  return VforkAllowlist.count(II);
120
44
}
121
122
void VforkChecker::reportBug(const char *What, CheckerContext &C,
123
18
                             const char *Details) const {
124
18
  if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
125
18
    if (!BT)
126
2
      BT.reset(new BuiltinBug(this,
127
2
                              "Dangerous construct in a vforked process"));
128
129
18
    SmallString<256> buf;
130
18
    llvm::raw_svector_ostream os(buf);
131
132
18
    os << What << " is prohibited after a successful vfork";
133
134
18
    if (Details)
135
4
      os << "; " << Details;
136
137
18
    auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
138
    // TODO: mark vfork call in BugReportVisitor
139
18
    C.emitReport(std::move(Report));
140
18
  }
141
18
}
142
143
// Detect calls to vfork and split execution appropriately.
144
void VforkChecker::checkPostCall(const CallEvent &Call,
145
1.24k
                                 CheckerContext &C) const {
146
  // We can't call vfork in child so don't bother
147
  // (corresponding warning has already been emitted in checkPreCall).
148
1.24k
  ProgramStateRef State = C.getState();
149
1.24k
  if (isChildProcess(State))
150
18
    return;
151
152
1.22k
  if (!isVforkCall(Call.getDecl(), C))
153
1.21k
    return;
154
155
  // Get return value of vfork.
156
14
  SVal VforkRetVal = Call.getReturnValue();
157
14
  Optional<DefinedOrUnknownSVal> DVal =
158
14
    VforkRetVal.getAs<DefinedOrUnknownSVal>();
159
14
  if (!DVal)
160
0
    return;
161
162
  // Get assigned variable.
163
14
  const ParentMap &PM = C.getLocationContext()->getParentMap();
164
14
  const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr());
165
14
  const VarDecl *LhsDecl;
166
14
  std::tie(LhsDecl, std::ignore) = parseAssignment(P);
167
168
  // Get assigned memory region.
169
14
  MemRegionManager &M = C.getStoreManager().getRegionManager();
170
14
  const MemRegion *LhsDeclReg =
171
14
    LhsDecl
172
14
      ? 
M.getVarRegion(LhsDecl, C.getLocationContext())6
173
14
      : 
(const MemRegion *)8
VFORK_RESULT_NONE8
;
174
175
  // Parent branch gets nonzero return value (according to manpage).
176
14
  ProgramStateRef ParentState, ChildState;
177
14
  std::tie(ParentState, ChildState) = C.getState()->assume(*DVal);
178
14
  C.addTransition(ParentState);
179
14
  ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg);
180
14
  C.addTransition(ChildState);
181
14
}
182
183
// Prohibit calls to functions in child process which are not explicitly
184
// allowed.
185
void VforkChecker::checkPreCall(const CallEvent &Call,
186
1.32k
                                CheckerContext &C) const {
187
1.32k
  ProgramStateRef State = C.getState();
188
1.32k
  if (isChildProcess(State) &&
189
1.32k
      
!isCallExplicitelyAllowed(Call.getCalleeIdentifier(), C)44
)
190
6
    reportBug("This function call", C);
191
1.32k
}
192
193
// Prohibit writes in child process (except for vfork's lhs).
194
void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S,
195
745
                             CheckerContext &C) const {
196
745
  ProgramStateRef State = C.getState();
197
745
  if (!isChildProcess(State))
198
727
    return;
199
200
18
  const MemRegion *VforkLhs =
201
18
    static_cast<const MemRegion *>(State->get<VforkResultRegion>());
202
18
  const MemRegion *MR = L.getAsRegion();
203
204
  // Child is allowed to modify only vfork's lhs.
205
18
  if (!MR || MR == VforkLhs)
206
10
    return;
207
208
8
  reportBug("This assignment", C);
209
8
}
210
211
// Prohibit return from function in child process.
212
153
void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const {
213
153
  ProgramStateRef State = C.getState();
214
153
  if (isChildProcess(State))
215
4
    reportBug("Return", C, "call _exit() instead");
216
153
}
217
218
65
void ento::registerVforkChecker(CheckerManager &mgr) {
219
65
  mgr.registerChecker<VforkChecker>();
220
65
}
221
222
134
bool ento::shouldRegisterVforkChecker(const CheckerManager &mgr) {
223
134
  return true;
224
134
}