brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.7 KiB · d622487 Raw
223 lines · cpp
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-exception6//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's11//  range of actions is significantly limited: can't write variables,12//  can't call functions not in the allowed list, etc. For more details, see13//  http://man7.org/linux/man-pages/man2/vfork.2.html14//15//  This checker checks for prohibited constructs in vforked process.16//  The state transition diagram:17//  PARENT ---(vfork() == 0)--> CHILD18//                                   |19//                                   --(*p = ...)--> bug20//                                   |21//                                   --foo()--> bug22//                                   |23//                                   --return--> bug24//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#include <optional>39 40using namespace clang;41using namespace ento;42 43namespace {44 45class VforkChecker : public Checker<check::PreCall, check::PostCall,46                                    check::Bind, check::PreStmt<ReturnStmt>> {47  const BugType BT{this, "Dangerous construct in a vforked process"};48  mutable llvm::SmallPtrSet<const IdentifierInfo *, 10> VforkAllowlist;49  mutable const IdentifierInfo *II_vfork = nullptr;50 51  static bool isChildProcess(const ProgramStateRef State);52 53  bool isVforkCall(const Decl *D, CheckerContext &C) const;54  bool isCallExplicitelyAllowed(const IdentifierInfo *II,55                                CheckerContext &C) const;56 57  void reportBug(const char *What, CheckerContext &C,58                 const char *Details = nullptr) const;59 60public:61  VforkChecker() = default;62 63  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;64  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;65  void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,66                 CheckerContext &C) const;67  void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;68};69 70} // end anonymous namespace71 72// This trait holds region of variable that is assigned with vfork's73// return value (this is the only region child is allowed to write).74// VFORK_RESULT_INVALID means that we are in parent process.75// VFORK_RESULT_NONE means that vfork's return value hasn't been assigned.76// Other values point to valid regions.77REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *)78#define VFORK_RESULT_INVALID 079#define VFORK_RESULT_NONE ((void *)(uintptr_t)1)80 81bool VforkChecker::isChildProcess(const ProgramStateRef State) {82  return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID;83}84 85bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {86  auto FD = dyn_cast_or_null<FunctionDecl>(D);87  if (!FD || !C.isCLibraryFunction(FD))88    return false;89 90  if (!II_vfork) {91    ASTContext &AC = C.getASTContext();92    II_vfork = &AC.Idents.get("vfork");93  }94 95  return FD->getIdentifier() == II_vfork;96}97 98// Returns true iff ok to call function after successful vfork.99bool VforkChecker::isCallExplicitelyAllowed(const IdentifierInfo *II,100                                            CheckerContext &C) const {101  if (VforkAllowlist.empty()) {102    // According to manpage.103    const char *ids[] = {104      "_Exit",105      "_exit",106      "execl",107      "execle",108      "execlp",109      "execv",110      "execve",111      "execvp",112      "execvpe",113      nullptr114    };115 116    ASTContext &AC = C.getASTContext();117    for (const char **id = ids; *id; ++id)118      VforkAllowlist.insert(&AC.Idents.get(*id));119  }120 121  return VforkAllowlist.count(II);122}123 124void VforkChecker::reportBug(const char *What, CheckerContext &C,125                             const char *Details) const {126  if (ExplodedNode *N = C.generateErrorNode(C.getState())) {127    SmallString<256> buf;128    llvm::raw_svector_ostream os(buf);129 130    os << What << " is prohibited after a successful vfork";131 132    if (Details)133      os << "; " << Details;134 135    auto Report = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);136    // TODO: mark vfork call in BugReportVisitor137    C.emitReport(std::move(Report));138  }139}140 141// Detect calls to vfork and split execution appropriately.142void VforkChecker::checkPostCall(const CallEvent &Call,143                                 CheckerContext &C) const {144  // We can't call vfork in child so don't bother145  // (corresponding warning has already been emitted in checkPreCall).146  ProgramStateRef State = C.getState();147  if (isChildProcess(State))148    return;149 150  if (!isVforkCall(Call.getDecl(), C))151    return;152 153  // Get return value of vfork.154  SVal VforkRetVal = Call.getReturnValue();155  std::optional<DefinedOrUnknownSVal> DVal =156      VforkRetVal.getAs<DefinedOrUnknownSVal>();157  if (!DVal)158    return;159 160  // Get assigned variable.161  const ParentMap &PM = C.getLocationContext()->getParentMap();162  const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr());163  const VarDecl *LhsDecl;164  std::tie(LhsDecl, std::ignore) = parseAssignment(P);165 166  // Get assigned memory region.167  MemRegionManager &M = C.getStoreManager().getRegionManager();168  const MemRegion *LhsDeclReg =169    LhsDecl170      ? M.getVarRegion(LhsDecl, C.getLocationContext())171      : (const MemRegion *)VFORK_RESULT_NONE;172 173  // Parent branch gets nonzero return value (according to manpage).174  ProgramStateRef ParentState, ChildState;175  std::tie(ParentState, ChildState) = C.getState()->assume(*DVal);176  C.addTransition(ParentState);177  ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg);178  C.addTransition(ChildState);179}180 181// Prohibit calls to functions in child process which are not explicitly182// allowed.183void VforkChecker::checkPreCall(const CallEvent &Call,184                                CheckerContext &C) const {185  ProgramStateRef State = C.getState();186  if (isChildProcess(State) &&187      !isCallExplicitelyAllowed(Call.getCalleeIdentifier(), C))188    reportBug("This function call", C);189}190 191// Prohibit writes in child process (except for vfork's lhs).192void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,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.210void 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 216void ento::registerVforkChecker(CheckerManager &mgr) {217  mgr.registerChecker<VforkChecker>();218}219 220bool ento::shouldRegisterVforkChecker(const CheckerManager &mgr) {221  return true;222}223