tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileDescriptorShuffle.cpp (3720B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FileDescriptorShuffle.h"
      8 
      9 #include "base/eintr_wrapper.h"
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/DebugOnly.h"
     12 
     13 #include <algorithm>
     14 #include <unistd.h>
     15 #include <fcntl.h>
     16 
     17 namespace mozilla {
     18 namespace ipc {
     19 
     20 // F_DUPFD_CLOEXEC is like F_DUPFD (see below) but atomically makes
     21 // the new fd close-on-exec.  This is useful to prevent accidental fd
     22 // leaks into processes created by plain fork/exec, but IPC uses
     23 // CloseSuperfluousFds so it's not essential.
     24 //
     25 // F_DUPFD_CLOEXEC is in POSIX 2008, but as of 2018 there are still
     26 // some OSes that don't support it.  (Specifically: Solaris 10 doesn't
     27 // have it, and Android should have kernel support but doesn't define
     28 // the constant until API 21 (Lollipop).  We also don't use this for
     29 // IPC child launching on Android, so there's no point in hard-coding
     30 // the definitions like we do for Android in some other cases.)
     31 #ifdef F_DUPFD_CLOEXEC
     32 static const int kDupFdCmd = F_DUPFD_CLOEXEC;
     33 #else
     34 static const int kDupFdCmd = F_DUPFD;
     35 #endif
     36 
     37 // This implementation ensures that the *ranges* of the source and
     38 // destination fds don't overlap, which is unnecessary but sufficient
     39 // to avoid conflicts or identity mappings.
     40 //
     41 // In practice, the source fds will usually be large and the
     42 // destination fds will all be relatively small, so there mostly won't
     43 // be temporary fds.  This approach has the advantage of being simple
     44 // (and linear-time, but hopefully there aren't enough fds for that to
     45 // matter).
     46 bool FileDescriptorShuffle::Init(MappingRef aMapping) {
     47  MOZ_ASSERT(mMapping.IsEmpty());
     48 
     49  // Find the maximum destination fd; any source fds not greater than
     50  // this will be duplicated.
     51  int maxDst = STDERR_FILENO;
     52  for (const auto& elem : aMapping) {
     53    maxDst = std::max(maxDst, elem.second);
     54  }
     55  mMaxDst = maxDst;
     56 
     57 #ifdef DEBUG
     58  // Increase the limit to make sure the F_DUPFD case gets test coverage.
     59  if (!aMapping.IsEmpty()) {
     60    // Try to find a value that will create a nontrivial partition.
     61    int fd0 = aMapping[0].first;
     62    int fdn = aMapping.rbegin()->first;
     63    maxDst = std::max(maxDst, (fd0 + fdn) / 2);
     64  }
     65 #endif
     66 
     67  for (const auto& elem : aMapping) {
     68    int src = elem.first;
     69    // F_DUPFD is like dup() but allows placing a lower bound
     70    // on the new fd, which is exactly what we want.
     71    if (src <= maxDst) {
     72      src = fcntl(src, kDupFdCmd, maxDst + 1);
     73      if (src < 0) {
     74        return false;
     75      }
     76      mTempFds.AppendElement(src);
     77    }
     78    MOZ_ASSERT(src > maxDst);
     79 #ifdef DEBUG
     80    // Check for accidentally mapping two different fds to the same
     81    // destination.  (This is O(n^2) time, but it shouldn't matter.)
     82    for (const auto& otherElem : mMapping) {
     83      MOZ_ASSERT(elem.second != otherElem.second);
     84    }
     85 #endif
     86    mMapping.AppendElement(std::make_pair(src, elem.second));
     87  }
     88  return true;
     89 }
     90 
     91 bool FileDescriptorShuffle::MapsTo(int aFd) const {
     92  // Prune fds that are too large to be a destination, rather than
     93  // searching; this should be the common case.
     94  if (aFd > mMaxDst) {
     95    return false;
     96  }
     97  for (const auto& elem : mMapping) {
     98    if (elem.second == aFd) {
     99      return true;
    100    }
    101  }
    102  return false;
    103 }
    104 
    105 FileDescriptorShuffle::~FileDescriptorShuffle() {
    106  for (const auto& fd : mTempFds) {
    107    mozilla::DebugOnly<int> rv = IGNORE_EINTR(close(fd));
    108    MOZ_ASSERT(rv == 0);
    109  }
    110 }
    111 
    112 }  // namespace ipc
    113 }  // namespace mozilla