/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 |