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