FileDescriptorShuffle.cpp (3720B)
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 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 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. 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 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