Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/ipc/glue/FileDescriptorShuffle.cpp
Line
Count
Source (jump to first uncovered line)
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
47
FileDescriptorShuffle::Init(MappingRef aMapping)
48
0
{
49
0
  MOZ_ASSERT(mMapping.IsEmpty());
50
0
51
0
  // Find the maximum destination fd; any source fds not greater than
52
0
  // this will be duplicated.
53
0
  int maxDst = STDERR_FILENO;
54
0
  for (const auto& elem : aMapping) {
55
0
    maxDst = std::max(maxDst, elem.second);
56
0
  }
57
0
  mMaxDst = maxDst;
58
0
59
#ifdef DEBUG
60
  // Increase the limit to make sure the F_DUPFD case gets test coverage.
61
  if (!aMapping.IsEmpty()) {
62
    // Try to find a value that will create a nontrivial partition.
63
    int fd0 = aMapping[0].first;
64
    int fdn = aMapping.rbegin()->first;
65
    maxDst = std::max(maxDst, (fd0 + fdn) / 2);
66
  }
67
#endif
68
69
0
  for (const auto& elem : aMapping) {
70
0
    int src = elem.first;
71
0
    // F_DUPFD is like dup() but allows placing a lower bound
72
0
    // on the new fd, which is exactly what we want.
73
0
    if (src <= maxDst) {
74
0
      src = fcntl(src, kDupFdCmd, maxDst + 1);
75
0
      if (src < 0) {
76
0
        return false;
77
0
      }
78
0
      mTempFds.AppendElement(src);
79
0
    }
80
0
    MOZ_ASSERT(src > maxDst);
81
#ifdef DEBUG
82
    // Check for accidentally mapping two different fds to the same
83
    // destination.  (This is O(n^2) time, but it shouldn't matter.)
84
    for (const auto& otherElem : mMapping) {
85
      MOZ_ASSERT(elem.second != otherElem.second);
86
    }
87
#endif
88
    mMapping.AppendElement(std::make_pair(src, elem.second));
89
0
  }
90
0
  return true;
91
0
}
92
93
bool
94
FileDescriptorShuffle::MapsTo(int aFd) const
95
0
{
96
0
  // Prune fds that are too large to be a destination, rather than
97
0
  // searching; this should be the common case.
98
0
  if (aFd > mMaxDst) {
99
0
    return false;
100
0
  }
101
0
  for (const auto& elem : mMapping) {
102
0
    if (elem.second == aFd) {
103
0
      return true;
104
0
    }
105
0
  }
106
0
  return false;
107
0
}
108
109
FileDescriptorShuffle::~FileDescriptorShuffle()
110
0
{
111
0
  for (const auto& fd : mTempFds) {
112
0
    mozilla::DebugOnly<int> rv = IGNORE_EINTR(close(fd));
113
0
    MOZ_ASSERT(rv == 0);
114
0
  }
115
0
}
116
117
} // namespace ipc
118
} // namespace mozilla