• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "mojo/public/cpp/platform/platform_channel.h"
6 
7 #include <cstddef>
8 #include <cstdint>
9 #include <string>
10 
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "build/build_config.h"
18 
19 #if defined(OS_WIN)
20 #include <windows.h>
21 
22 #include "base/win/scoped_handle.h"
23 #elif defined(OS_FUCHSIA)
24 #include <lib/zx/channel.h>
25 #include <zircon/process.h>
26 #include <zircon/processargs.h>
27 
28 #include "base/fuchsia/fuchsia_logging.h"
29 #elif defined(OS_POSIX)
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #include "base/files/scoped_file.h"
35 #include "base/posix/global_descriptors.h"
36 #endif
37 
38 #if defined(OS_POSIX) && !defined(OS_NACL_SFI)
39 #include <sys/socket.h>
40 #elif defined(OS_NACL_SFI)
41 #include "native_client/src/public/imc_syscalls.h"
42 #endif
43 
44 namespace mojo {
45 
46 namespace {
47 
48 #if defined(OS_WIN)
CreateChannel(PlatformHandle * local_endpoint,PlatformHandle * remote_endpoint)49 void CreateChannel(PlatformHandle* local_endpoint,
50                    PlatformHandle* remote_endpoint) {
51   base::string16 pipe_name = base::UTF8ToUTF16(base::StringPrintf(
52       "\\\\.\\pipe\\mojo.%lu.%lu.%I64u", ::GetCurrentProcessId(),
53       ::GetCurrentThreadId(), base::RandUint64()));
54   DWORD kOpenMode =
55       PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
56   const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
57   *local_endpoint = PlatformHandle(base::win::ScopedHandle(
58       ::CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode,
59                          1,           // Max instances.
60                          4096,        // Output buffer size.
61                          4096,        // Input buffer size.
62                          5000,        // Timeout in ms.
63                          nullptr)));  // Default security descriptor.
64   PCHECK(local_endpoint->is_valid());
65 
66   const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
67   // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
68   // the client.
69   DWORD kFlags =
70       SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
71   // Allow the handle to be inherited by child processes.
72   SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
73                                              nullptr, TRUE};
74   *remote_endpoint = PlatformHandle(base::win::ScopedHandle(
75       ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, &security_attributes,
76                     OPEN_EXISTING, kFlags, nullptr)));
77   PCHECK(remote_endpoint->is_valid());
78 
79   // Since a client has connected, ConnectNamedPipe() should return zero and
80   // GetLastError() should return ERROR_PIPE_CONNECTED.
81   CHECK(!::ConnectNamedPipe(local_endpoint->GetHandle().Get(), nullptr));
82   PCHECK(::GetLastError() == ERROR_PIPE_CONNECTED);
83 }
84 #elif defined(OS_FUCHSIA)
85 void CreateChannel(PlatformHandle* local_endpoint,
86                    PlatformHandle* remote_endpoint) {
87   zx::channel handles[2];
88   zx_status_t result = zx::channel::create(0, &handles[0], &handles[1]);
89   ZX_CHECK(result == ZX_OK, result);
90 
91   *local_endpoint = PlatformHandle(std::move(handles[0]));
92   *remote_endpoint = PlatformHandle(std::move(handles[1]));
93   DCHECK(local_endpoint->is_valid());
94   DCHECK(remote_endpoint->is_valid());
95 }
96 #elif defined(OS_POSIX)
97 
98 #if defined(OS_ANDROID)
99 // Leave room for any other descriptors defined in content for example.
100 // TODO(https://crbug.com/676442): Consider changing base::GlobalDescriptors to
101 // generate a key when setting the file descriptor.
102 constexpr int kAndroidClientHandleDescriptor =
103     base::GlobalDescriptors::kBaseDescriptor + 10000;
104 #else
105 bool IsTargetDescriptorUsed(const base::FileHandleMappingVector& mapping,
106                             int target_fd) {
107   for (size_t i = 0; i < mapping.size(); ++i) {
108     if (mapping[i].second == target_fd)
109       return true;
110   }
111   return false;
112 }
113 #endif
114 
115 void CreateChannel(PlatformHandle* local_endpoint,
116                    PlatformHandle* remote_endpoint) {
117   int fds[2];
118 #if defined(OS_NACL_SFI)
119   PCHECK(imc_socketpair(fds) == 0);
120 #else
121   PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
122 
123   // Set non-blocking on both ends.
124   PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
125   PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
126 
127 #if defined(OS_MACOSX)
128   // This turns off |SIGPIPE| when writing to a closed socket, causing the call
129   // to fail with |EPIPE| instead. On Linux we have to use |send...()| with
130   // |MSG_NOSIGNAL| instead, which is not supported on Mac.
131   int no_sigpipe = 1;
132   PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
133                     sizeof(no_sigpipe)) == 0);
134   PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
135                     sizeof(no_sigpipe)) == 0);
136 #endif  // defined(OS_MACOSX)
137 #endif  // defined(OS_NACL_SFI)
138 
139   *local_endpoint = PlatformHandle(base::ScopedFD(fds[0]));
140   *remote_endpoint = PlatformHandle(base::ScopedFD(fds[1]));
141   DCHECK(local_endpoint->is_valid());
142   DCHECK(remote_endpoint->is_valid());
143 }
144 #else
145 #error "Unsupported platform."
146 #endif
147 
148 }  // namespace
149 
150 const char PlatformChannel::kHandleSwitch[] = "mojo-platform-channel-handle";
151 
PlatformChannel()152 PlatformChannel::PlatformChannel() {
153   PlatformHandle local_handle;
154   PlatformHandle remote_handle;
155   CreateChannel(&local_handle, &remote_handle);
156   local_endpoint_ = PlatformChannelEndpoint(std::move(local_handle));
157   remote_endpoint_ = PlatformChannelEndpoint(std::move(remote_handle));
158 }
159 
160 PlatformChannel::PlatformChannel(PlatformChannel&& other) = default;
161 
162 PlatformChannel::~PlatformChannel() = default;
163 
164 PlatformChannel& PlatformChannel::operator=(PlatformChannel&& other) = default;
165 
PrepareToPassRemoteEndpoint(HandlePassingInfo * info,std::string * value)166 void PlatformChannel::PrepareToPassRemoteEndpoint(HandlePassingInfo* info,
167                                                   std::string* value) {
168   DCHECK(info);
169   DCHECK(value);
170   DCHECK(remote_endpoint_.is_valid());
171 
172 #if defined(OS_WIN)
173   info->push_back(remote_endpoint_.platform_handle().GetHandle().Get());
174   *value = base::NumberToString(
175       HandleToLong(remote_endpoint_.platform_handle().GetHandle().Get()));
176 #elif defined(OS_FUCHSIA)
177   const uint32_t id = PA_HND(PA_USER0, info->size());
178   info->push_back({id, remote_endpoint_.platform_handle().GetHandle().get()});
179   *value = base::NumberToString(id);
180 #elif defined(OS_ANDROID)
181   int fd = remote_endpoint_.platform_handle().GetFD().get();
182   int mapped_fd = kAndroidClientHandleDescriptor + info->size();
183   info->emplace_back(fd, mapped_fd);
184   *value = base::NumberToString(mapped_fd);
185 #elif defined(OS_POSIX)
186   // Arbitrary sanity check to ensure the loop below terminates reasonably
187   // quickly.
188   CHECK_LT(info->size(), 1000u);
189 
190   // Find a suitable FD to map the remote endpoint handle to in the child
191   // process. This has quadratic time complexity in the size of |*info|, but
192   // |*info| should be very small and is usually empty.
193   int target_fd = base::GlobalDescriptors::kBaseDescriptor;
194   while (IsTargetDescriptorUsed(*info, target_fd))
195     ++target_fd;
196   info->emplace_back(remote_endpoint_.platform_handle().GetFD().get(),
197                      target_fd);
198   *value = base::NumberToString(target_fd);
199 #endif
200 }
201 
PrepareToPassRemoteEndpoint(HandlePassingInfo * info,base::CommandLine * command_line)202 void PlatformChannel::PrepareToPassRemoteEndpoint(
203     HandlePassingInfo* info,
204     base::CommandLine* command_line) {
205   std::string value;
206   PrepareToPassRemoteEndpoint(info, &value);
207   if (!value.empty())
208     command_line->AppendSwitchASCII(kHandleSwitch, value);
209 }
210 
PrepareToPassRemoteEndpoint(base::LaunchOptions * options,base::CommandLine * command_line)211 void PlatformChannel::PrepareToPassRemoteEndpoint(
212     base::LaunchOptions* options,
213     base::CommandLine* command_line) {
214 #if defined(OS_WIN)
215   PrepareToPassRemoteEndpoint(&options->handles_to_inherit, command_line);
216 #elif defined(OS_FUCHSIA)
217   PrepareToPassRemoteEndpoint(&options->handles_to_transfer, command_line);
218 #elif defined(OS_POSIX)
219   PrepareToPassRemoteEndpoint(&options->fds_to_remap, command_line);
220 #else
221 #error "Platform not supported."
222 #endif
223 }
224 
RemoteProcessLaunchAttempted()225 void PlatformChannel::RemoteProcessLaunchAttempted() {
226 #if defined(OS_FUCHSIA)
227   // Unlike other platforms, Fuchsia transfers handle ownership to the new
228   // process, rather than duplicating it. For consistency the process-launch
229   // call will have consumed the handle regardless of whether launch succeeded.
230   DCHECK(remote_endpoint_.platform_handle().is_valid_handle());
231   ignore_result(remote_endpoint_.TakePlatformHandle().ReleaseHandle());
232 #else
233   remote_endpoint_.reset();
234 #endif
235 }
236 
237 // static
RecoverPassedEndpointFromString(base::StringPiece value)238 PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromString(
239     base::StringPiece value) {
240 #if defined(OS_WIN)
241   int handle_value = 0;
242   if (value.empty() || !base::StringToInt(value, &handle_value)) {
243     DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
244     return PlatformChannelEndpoint();
245   }
246   return PlatformChannelEndpoint(
247       PlatformHandle(base::win::ScopedHandle(LongToHandle(handle_value))));
248 #elif defined(OS_FUCHSIA)
249   unsigned int handle_value = 0;
250   if (value.empty() || !base::StringToUint(value, &handle_value)) {
251     DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
252     return PlatformChannelEndpoint();
253   }
254   return PlatformChannelEndpoint(PlatformHandle(zx::handle(
255       zx_take_startup_handle(base::checked_cast<uint32_t>(handle_value)))));
256 #elif defined(OS_ANDROID)
257   base::GlobalDescriptors::Key key = -1;
258   if (value.empty() || !base::StringToUint(value, &key)) {
259     DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
260     return PlatformChannelEndpoint();
261   }
262   return PlatformChannelEndpoint(PlatformHandle(
263       base::ScopedFD(base::GlobalDescriptors::GetInstance()->Get(key))));
264 #elif defined(OS_POSIX)
265   int fd = -1;
266   if (value.empty() || !base::StringToInt(value, &fd) ||
267       fd < base::GlobalDescriptors::kBaseDescriptor) {
268     DLOG(ERROR) << "Invalid PlatformChannel endpoint string.";
269     return PlatformChannelEndpoint();
270   }
271   return PlatformChannelEndpoint(PlatformHandle(base::ScopedFD(fd)));
272 #endif
273 }
274 
275 // static
RecoverPassedEndpointFromCommandLine(const base::CommandLine & command_line)276 PlatformChannelEndpoint PlatformChannel::RecoverPassedEndpointFromCommandLine(
277     const base::CommandLine& command_line) {
278   return RecoverPassedEndpointFromString(
279       command_line.GetSwitchValueASCII(kHandleSwitch));
280 }
281 
282 }  // namespace mojo
283