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).
Init(MappingRef aMapping)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 
MapsTo(int aFd) const91 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 
~FileDescriptorShuffle()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
114