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/. */
7 #include "FileDescriptorShuffle.h"
9 #include "base/eintr_wrapper.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/DebugOnly.h"
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.
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
;
34 static const int kDupFdCmd
= F_DUPFD
;
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.
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
46 bool FileDescriptorShuffle::Init(MappingRef aMapping
) {
47 MOZ_ASSERT(mMapping
.IsEmpty());
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
);
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);
67 for (const auto& elem
: aMapping
) {
69 // F_DUPFD is like dup() but allows placing a lower bound
70 // on the new fd, which is exactly what we want.
72 src
= fcntl(src
, kDupFdCmd
, maxDst
+ 1);
76 mTempFds
.AppendElement(src
);
78 MOZ_ASSERT(src
> maxDst
);
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
);
86 mMapping
.AppendElement(std::make_pair(src
, elem
.second
));
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.
97 for (const auto& elem
: mMapping
) {
98 if (elem
.second
== aFd
) {
105 FileDescriptorShuffle::~FileDescriptorShuffle() {
106 for (const auto& fd
: mTempFds
) {
107 mozilla::DebugOnly
<int> rv
= IGNORE_EINTR(close(fd
));
113 } // namespace mozilla