xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===- VforkChecker.cpp -------- Vfork usage checks --------------*- C++ -*-==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg //  This file defines vfork checker which checks for dangerous uses of vfork.
107330f729Sjoerg //  Vforked process shares memory (including stack) with parent so it's
117330f729Sjoerg //  range of actions is significantly limited: can't write variables,
127330f729Sjoerg //  can't call functions not in whitelist, etc. For more details, see
137330f729Sjoerg //  http://man7.org/linux/man-pages/man2/vfork.2.html
147330f729Sjoerg //
157330f729Sjoerg //  This checker checks for prohibited constructs in vforked process.
167330f729Sjoerg //  The state transition diagram:
177330f729Sjoerg //  PARENT ---(vfork() == 0)--> CHILD
187330f729Sjoerg //                                   |
197330f729Sjoerg //                                   --(*p = ...)--> bug
207330f729Sjoerg //                                   |
217330f729Sjoerg //                                   --foo()--> bug
227330f729Sjoerg //                                   |
237330f729Sjoerg //                                   --return--> bug
247330f729Sjoerg //
257330f729Sjoerg //===----------------------------------------------------------------------===//
267330f729Sjoerg 
277330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
287330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
297330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
307330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
317330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
327330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
337330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
347330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
357330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
367330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
377330f729Sjoerg #include "clang/AST/ParentMap.h"
387330f729Sjoerg 
397330f729Sjoerg using namespace clang;
407330f729Sjoerg using namespace ento;
417330f729Sjoerg 
427330f729Sjoerg namespace {
437330f729Sjoerg 
447330f729Sjoerg class VforkChecker : public Checker<check::PreCall, check::PostCall,
457330f729Sjoerg                                     check::Bind, check::PreStmt<ReturnStmt>> {
467330f729Sjoerg   mutable std::unique_ptr<BuiltinBug> BT;
477330f729Sjoerg   mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist;
487330f729Sjoerg   mutable const IdentifierInfo *II_vfork;
497330f729Sjoerg 
507330f729Sjoerg   static bool isChildProcess(const ProgramStateRef State);
517330f729Sjoerg 
527330f729Sjoerg   bool isVforkCall(const Decl *D, CheckerContext &C) const;
537330f729Sjoerg   bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const;
547330f729Sjoerg 
557330f729Sjoerg   void reportBug(const char *What, CheckerContext &C,
567330f729Sjoerg                  const char *Details = nullptr) const;
577330f729Sjoerg 
587330f729Sjoerg public:
VforkChecker()597330f729Sjoerg   VforkChecker() : II_vfork(nullptr) {}
607330f729Sjoerg 
617330f729Sjoerg   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
627330f729Sjoerg   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
637330f729Sjoerg   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
647330f729Sjoerg   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
657330f729Sjoerg };
667330f729Sjoerg 
677330f729Sjoerg } // end anonymous namespace
687330f729Sjoerg 
697330f729Sjoerg // This trait holds region of variable that is assigned with vfork's
707330f729Sjoerg // return value (this is the only region child is allowed to write).
717330f729Sjoerg // VFORK_RESULT_INVALID means that we are in parent process.
727330f729Sjoerg // VFORK_RESULT_NONE means that vfork's return value hasn't been assigned.
737330f729Sjoerg // Other values point to valid regions.
REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion,const void *)747330f729Sjoerg REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *)
757330f729Sjoerg #define VFORK_RESULT_INVALID 0
767330f729Sjoerg #define VFORK_RESULT_NONE ((void *)(uintptr_t)1)
777330f729Sjoerg 
787330f729Sjoerg bool VforkChecker::isChildProcess(const ProgramStateRef State) {
797330f729Sjoerg   return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID;
807330f729Sjoerg }
817330f729Sjoerg 
isVforkCall(const Decl * D,CheckerContext & C) const827330f729Sjoerg bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {
837330f729Sjoerg   auto FD = dyn_cast_or_null<FunctionDecl>(D);
847330f729Sjoerg   if (!FD || !C.isCLibraryFunction(FD))
857330f729Sjoerg     return false;
867330f729Sjoerg 
877330f729Sjoerg   if (!II_vfork) {
887330f729Sjoerg     ASTContext &AC = C.getASTContext();
897330f729Sjoerg     II_vfork = &AC.Idents.get("vfork");
907330f729Sjoerg   }
917330f729Sjoerg 
927330f729Sjoerg   return FD->getIdentifier() == II_vfork;
937330f729Sjoerg }
947330f729Sjoerg 
957330f729Sjoerg // Returns true iff ok to call function after successful vfork.
isCallWhitelisted(const IdentifierInfo * II,CheckerContext & C) const967330f729Sjoerg bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
977330f729Sjoerg                                  CheckerContext &C) const {
987330f729Sjoerg   if (VforkWhitelist.empty()) {
997330f729Sjoerg     // According to manpage.
1007330f729Sjoerg     const char *ids[] = {
1017330f729Sjoerg       "_Exit",
102*e038c9c4Sjoerg       "_exit",
1037330f729Sjoerg       "execl",
1047330f729Sjoerg       "execle",
105*e038c9c4Sjoerg       "execlp",
1067330f729Sjoerg       "execv",
107*e038c9c4Sjoerg       "execve",
1087330f729Sjoerg       "execvp",
1097330f729Sjoerg       "execvpe",
1107330f729Sjoerg       nullptr
1117330f729Sjoerg     };
1127330f729Sjoerg 
1137330f729Sjoerg     ASTContext &AC = C.getASTContext();
1147330f729Sjoerg     for (const char **id = ids; *id; ++id)
1157330f729Sjoerg       VforkWhitelist.insert(&AC.Idents.get(*id));
1167330f729Sjoerg   }
1177330f729Sjoerg 
1187330f729Sjoerg   return VforkWhitelist.count(II);
1197330f729Sjoerg }
1207330f729Sjoerg 
reportBug(const char * What,CheckerContext & C,const char * Details) const1217330f729Sjoerg void VforkChecker::reportBug(const char *What, CheckerContext &C,
1227330f729Sjoerg                              const char *Details) const {
1237330f729Sjoerg   if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
1247330f729Sjoerg     if (!BT)
1257330f729Sjoerg       BT.reset(new BuiltinBug(this,
1267330f729Sjoerg                               "Dangerous construct in a vforked process"));
1277330f729Sjoerg 
1287330f729Sjoerg     SmallString<256> buf;
1297330f729Sjoerg     llvm::raw_svector_ostream os(buf);
1307330f729Sjoerg 
1317330f729Sjoerg     os << What << " is prohibited after a successful vfork";
1327330f729Sjoerg 
1337330f729Sjoerg     if (Details)
1347330f729Sjoerg       os << "; " << Details;
1357330f729Sjoerg 
1367330f729Sjoerg     auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
1377330f729Sjoerg     // TODO: mark vfork call in BugReportVisitor
1387330f729Sjoerg     C.emitReport(std::move(Report));
1397330f729Sjoerg   }
1407330f729Sjoerg }
1417330f729Sjoerg 
1427330f729Sjoerg // Detect calls to vfork and split execution appropriately.
checkPostCall(const CallEvent & Call,CheckerContext & C) const1437330f729Sjoerg void VforkChecker::checkPostCall(const CallEvent &Call,
1447330f729Sjoerg                                  CheckerContext &C) const {
1457330f729Sjoerg   // We can't call vfork in child so don't bother
1467330f729Sjoerg   // (corresponding warning has already been emitted in checkPreCall).
1477330f729Sjoerg   ProgramStateRef State = C.getState();
1487330f729Sjoerg   if (isChildProcess(State))
1497330f729Sjoerg     return;
1507330f729Sjoerg 
1517330f729Sjoerg   if (!isVforkCall(Call.getDecl(), C))
1527330f729Sjoerg     return;
1537330f729Sjoerg 
1547330f729Sjoerg   // Get return value of vfork.
1557330f729Sjoerg   SVal VforkRetVal = Call.getReturnValue();
1567330f729Sjoerg   Optional<DefinedOrUnknownSVal> DVal =
1577330f729Sjoerg     VforkRetVal.getAs<DefinedOrUnknownSVal>();
1587330f729Sjoerg   if (!DVal)
1597330f729Sjoerg     return;
1607330f729Sjoerg 
1617330f729Sjoerg   // Get assigned variable.
1627330f729Sjoerg   const ParentMap &PM = C.getLocationContext()->getParentMap();
1637330f729Sjoerg   const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr());
1647330f729Sjoerg   const VarDecl *LhsDecl;
1657330f729Sjoerg   std::tie(LhsDecl, std::ignore) = parseAssignment(P);
1667330f729Sjoerg 
1677330f729Sjoerg   // Get assigned memory region.
1687330f729Sjoerg   MemRegionManager &M = C.getStoreManager().getRegionManager();
1697330f729Sjoerg   const MemRegion *LhsDeclReg =
1707330f729Sjoerg     LhsDecl
1717330f729Sjoerg       ? M.getVarRegion(LhsDecl, C.getLocationContext())
1727330f729Sjoerg       : (const MemRegion *)VFORK_RESULT_NONE;
1737330f729Sjoerg 
1747330f729Sjoerg   // Parent branch gets nonzero return value (according to manpage).
1757330f729Sjoerg   ProgramStateRef ParentState, ChildState;
1767330f729Sjoerg   std::tie(ParentState, ChildState) = C.getState()->assume(*DVal);
1777330f729Sjoerg   C.addTransition(ParentState);
1787330f729Sjoerg   ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg);
1797330f729Sjoerg   C.addTransition(ChildState);
1807330f729Sjoerg }
1817330f729Sjoerg 
1827330f729Sjoerg // Prohibit calls to non-whitelist functions in child process.
checkPreCall(const CallEvent & Call,CheckerContext & C) const1837330f729Sjoerg void VforkChecker::checkPreCall(const CallEvent &Call,
1847330f729Sjoerg                                 CheckerContext &C) const {
1857330f729Sjoerg   ProgramStateRef State = C.getState();
1867330f729Sjoerg   if (isChildProcess(State)
1877330f729Sjoerg       && !isCallWhitelisted(Call.getCalleeIdentifier(), C))
1887330f729Sjoerg     reportBug("This function call", C);
1897330f729Sjoerg }
1907330f729Sjoerg 
1917330f729Sjoerg // Prohibit writes in child process (except for vfork's lhs).
checkBind(SVal L,SVal V,const Stmt * S,CheckerContext & C) const1927330f729Sjoerg void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S,
1937330f729Sjoerg                              CheckerContext &C) const {
1947330f729Sjoerg   ProgramStateRef State = C.getState();
1957330f729Sjoerg   if (!isChildProcess(State))
1967330f729Sjoerg     return;
1977330f729Sjoerg 
1987330f729Sjoerg   const MemRegion *VforkLhs =
1997330f729Sjoerg     static_cast<const MemRegion *>(State->get<VforkResultRegion>());
2007330f729Sjoerg   const MemRegion *MR = L.getAsRegion();
2017330f729Sjoerg 
2027330f729Sjoerg   // Child is allowed to modify only vfork's lhs.
2037330f729Sjoerg   if (!MR || MR == VforkLhs)
2047330f729Sjoerg     return;
2057330f729Sjoerg 
2067330f729Sjoerg   reportBug("This assignment", C);
2077330f729Sjoerg }
2087330f729Sjoerg 
2097330f729Sjoerg // Prohibit return from function in child process.
checkPreStmt(const ReturnStmt * RS,CheckerContext & C) const2107330f729Sjoerg void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const {
2117330f729Sjoerg   ProgramStateRef State = C.getState();
2127330f729Sjoerg   if (isChildProcess(State))
2137330f729Sjoerg     reportBug("Return", C, "call _exit() instead");
2147330f729Sjoerg }
2157330f729Sjoerg 
registerVforkChecker(CheckerManager & mgr)2167330f729Sjoerg void ento::registerVforkChecker(CheckerManager &mgr) {
2177330f729Sjoerg   mgr.registerChecker<VforkChecker>();
2187330f729Sjoerg }
2197330f729Sjoerg 
shouldRegisterVforkChecker(const CheckerManager & mgr)220*e038c9c4Sjoerg bool ento::shouldRegisterVforkChecker(const CheckerManager &mgr) {
2217330f729Sjoerg   return true;
2227330f729Sjoerg }
223