summaryrefslogtreecommitdiffstats
path: root/ipc/glue/FileDescriptorShuffle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/FileDescriptorShuffle.cpp')
-rw-r--r--ipc/glue/FileDescriptorShuffle.cpp113
1 files changed, 113 insertions, 0 deletions
diff --git a/ipc/glue/FileDescriptorShuffle.cpp b/ipc/glue/FileDescriptorShuffle.cpp
new file mode 100644
index 0000000000..7ce89e1b16
--- /dev/null
+++ b/ipc/glue/FileDescriptorShuffle.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileDescriptorShuffle.h"
+
+#include "base/eintr_wrapper.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+
+#include <algorithm>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace mozilla {
+namespace ipc {
+
+// F_DUPFD_CLOEXEC is like F_DUPFD (see below) but atomically makes
+// the new fd close-on-exec. This is useful to prevent accidental fd
+// leaks into processes created by plain fork/exec, but IPC uses
+// CloseSuperfluousFds so it's not essential.
+//
+// F_DUPFD_CLOEXEC is in POSIX 2008, but as of 2018 there are still
+// some OSes that don't support it. (Specifically: Solaris 10 doesn't
+// have it, and Android should have kernel support but doesn't define
+// the constant until API 21 (Lollipop). We also don't use this for
+// IPC child launching on Android, so there's no point in hard-coding
+// the definitions like we do for Android in some other cases.)
+#ifdef F_DUPFD_CLOEXEC
+static const int kDupFdCmd = F_DUPFD_CLOEXEC;
+#else
+static const int kDupFdCmd = F_DUPFD;
+#endif
+
+// This implementation ensures that the *ranges* of the source and
+// destination fds don't overlap, which is unnecessary but sufficient
+// to avoid conflicts or identity mappings.
+//
+// In practice, the source fds will usually be large and the
+// destination fds will all be relatively small, so there mostly won't
+// be temporary fds. This approach has the advantage of being simple
+// (and linear-time, but hopefully there aren't enough fds for that to
+// matter).
+bool FileDescriptorShuffle::Init(MappingRef aMapping) {
+ MOZ_ASSERT(mMapping.IsEmpty());
+
+ // Find the maximum destination fd; any source fds not greater than
+ // this will be duplicated.
+ int maxDst = STDERR_FILENO;
+ for (const auto& elem : aMapping) {
+ maxDst = std::max(maxDst, elem.second);
+ }
+ mMaxDst = maxDst;
+
+#ifdef DEBUG
+ // Increase the limit to make sure the F_DUPFD case gets test coverage.
+ if (!aMapping.IsEmpty()) {
+ // Try to find a value that will create a nontrivial partition.
+ int fd0 = aMapping[0].first;
+ int fdn = aMapping.rbegin()->first;
+ maxDst = std::max(maxDst, (fd0 + fdn) / 2);
+ }
+#endif
+
+ for (const auto& elem : aMapping) {
+ int src = elem.first;
+ // F_DUPFD is like dup() but allows placing a lower bound
+ // on the new fd, which is exactly what we want.
+ if (src <= maxDst) {
+ src = fcntl(src, kDupFdCmd, maxDst + 1);
+ if (src < 0) {
+ return false;
+ }
+ mTempFds.AppendElement(src);
+ }
+ MOZ_ASSERT(src > maxDst);
+#ifdef DEBUG
+ // Check for accidentally mapping two different fds to the same
+ // destination. (This is O(n^2) time, but it shouldn't matter.)
+ for (const auto& otherElem : mMapping) {
+ MOZ_ASSERT(elem.second != otherElem.second);
+ }
+#endif
+ mMapping.AppendElement(std::make_pair(src, elem.second));
+ }
+ return true;
+}
+
+bool FileDescriptorShuffle::MapsTo(int aFd) const {
+ // Prune fds that are too large to be a destination, rather than
+ // searching; this should be the common case.
+ if (aFd > mMaxDst) {
+ return false;
+ }
+ for (const auto& elem : mMapping) {
+ if (elem.second == aFd) {
+ return true;
+ }
+ }
+ return false;
+}
+
+FileDescriptorShuffle::~FileDescriptorShuffle() {
+ for (const auto& fd : mTempFds) {
+ mozilla::DebugOnly<int> rv = IGNORE_EINTR(close(fd));
+ MOZ_ASSERT(rv == 0);
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla