• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 "ipc/ipc_channel_nacl.h"
6 
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 #include <sys/types.h>
11 
12 #include <algorithm>
13 
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/message_loop/message_pump_for_io.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/synchronization/lock.h"
21 #include "base/task_runner_util.h"
22 #include "base/threading/simple_thread.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "ipc/ipc_listener.h"
25 #include "ipc/ipc_logging.h"
26 #include "ipc/ipc_message_attachment_set.h"
27 #include "ipc/ipc_platform_file_attachment_posix.h"
28 #include "native_client/src/public/imc_syscalls.h"
29 #include "native_client/src/public/imc_types.h"
30 
31 namespace IPC {
32 
33 struct MessageContents {
34   std::vector<char> data;
35   std::vector<int> fds;
36 };
37 
38 namespace {
39 
ReadDataOnReaderThread(int pipe,MessageContents * contents)40 bool ReadDataOnReaderThread(int pipe, MessageContents* contents) {
41   DCHECK(pipe >= 0);
42   if (pipe < 0)
43     return false;
44 
45   contents->data.resize(Channel::kReadBufferSize);
46   contents->fds.resize(NACL_ABI_IMC_DESC_MAX);
47 
48   NaClAbiNaClImcMsgIoVec iov = { &contents->data[0], contents->data.size() };
49   NaClAbiNaClImcMsgHdr msg = {
50     &iov, 1, &contents->fds[0], contents->fds.size()
51   };
52 
53   int bytes_read = imc_recvmsg(pipe, &msg, 0);
54 
55   if (bytes_read <= 0) {
56     // NaClIPCAdapter::BlockingReceive returns -1 when the pipe closes (either
57     // due to error or for regular shutdown).
58     contents->data.clear();
59     contents->fds.clear();
60     return false;
61   }
62   DCHECK(bytes_read);
63   // Resize the buffers down to the number of bytes and fds we actually read.
64   contents->data.resize(bytes_read);
65   contents->fds.resize(msg.desc_length);
66   return true;
67 }
68 
69 }  // namespace
70 
71 // static
72 constexpr size_t Channel::kMaximumMessageSize;
73 
74 class ChannelNacl::ReaderThreadRunner
75     : public base::DelegateSimpleThread::Delegate {
76  public:
77   // |pipe|: A file descriptor from which we will read using imc_recvmsg.
78   // |data_read_callback|: A callback we invoke (on the main thread) when we
79   //                       have read data.
80   // |failure_callback|: A callback we invoke when we have a failure reading
81   //                     from |pipe|.
82   // |main_message_loop|: A proxy for the main thread, where we will invoke the
83   //                      above callbacks.
84   ReaderThreadRunner(
85       int pipe,
86       base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback,
87       base::Callback<void()> failure_callback,
88       scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
89 
90   // DelegateSimpleThread implementation. Reads data from the pipe in a loop
91   // until either we are told to quit or a read fails.
92   void Run() override;
93 
94  private:
95   int pipe_;
96   base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback_;
97   base::Callback<void ()> failure_callback_;
98   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
99 
100   DISALLOW_COPY_AND_ASSIGN(ReaderThreadRunner);
101 };
102 
ReaderThreadRunner(int pipe,base::Callback<void (std::unique_ptr<MessageContents>)> data_read_callback,base::Callback<void ()> failure_callback,scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)103 ChannelNacl::ReaderThreadRunner::ReaderThreadRunner(
104     int pipe,
105     base::Callback<void(std::unique_ptr<MessageContents>)> data_read_callback,
106     base::Callback<void()> failure_callback,
107     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
108     : pipe_(pipe),
109       data_read_callback_(data_read_callback),
110       failure_callback_(failure_callback),
111       main_task_runner_(main_task_runner) {}
112 
Run()113 void ChannelNacl::ReaderThreadRunner::Run() {
114   while (true) {
115     std::unique_ptr<MessageContents> msg_contents(new MessageContents);
116     bool success = ReadDataOnReaderThread(pipe_, msg_contents.get());
117     if (success) {
118       main_task_runner_->PostTask(
119           FROM_HERE,
120           base::Bind(data_read_callback_, base::Passed(&msg_contents)));
121     } else {
122       main_task_runner_->PostTask(FROM_HERE, failure_callback_);
123       // Because the read failed, we know we're going to quit. Don't bother
124       // trying to read again.
125       return;
126     }
127   }
128 }
129 
ChannelNacl(const IPC::ChannelHandle & channel_handle,Mode mode,Listener * listener)130 ChannelNacl::ChannelNacl(const IPC::ChannelHandle& channel_handle,
131                          Mode mode,
132                          Listener* listener)
133     : ChannelReader(listener),
134       mode_(mode),
135       waiting_connect_(true),
136       pipe_(-1),
137       weak_ptr_factory_(this) {
138   if (!CreatePipe(channel_handle)) {
139     // The pipe may have been closed already.
140     const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client";
141     LOG(WARNING) << "Unable to create pipe in " << modestr << " mode";
142   }
143 }
144 
~ChannelNacl()145 ChannelNacl::~ChannelNacl() {
146   CleanUp();
147   Close();
148 }
149 
Connect()150 bool ChannelNacl::Connect() {
151   WillConnect();
152 
153   if (pipe_ == -1) {
154     DLOG(WARNING) << "Channel creation failed";
155     return false;
156   }
157 
158   // Note that Connect is called on the "Channel" thread (i.e., the same thread
159   // where Channel::Send will be called, and the same thread that should receive
160   // messages). The constructor might be invoked on another thread (see
161   // ChannelProxy for an example of that). Therefore, we must wait until Connect
162   // is called to decide which SingleThreadTaskRunner to pass to
163   // ReaderThreadRunner.
164   reader_thread_runner_.reset(new ReaderThreadRunner(
165       pipe_,
166       base::Bind(&ChannelNacl::DidRecvMsg, weak_ptr_factory_.GetWeakPtr()),
167       base::Bind(&ChannelNacl::ReadDidFail, weak_ptr_factory_.GetWeakPtr()),
168       base::ThreadTaskRunnerHandle::Get()));
169   reader_thread_.reset(
170       new base::DelegateSimpleThread(reader_thread_runner_.get(),
171                                      "ipc_channel_nacl reader thread"));
172   reader_thread_->Start();
173   waiting_connect_ = false;
174   // If there were any messages queued before connection, send them.
175   ProcessOutgoingMessages();
176   base::ThreadTaskRunnerHandle::Get()->PostTask(
177       FROM_HERE, base::Bind(&ChannelNacl::CallOnChannelConnected,
178                             weak_ptr_factory_.GetWeakPtr()));
179 
180   return true;
181 }
182 
Close()183 void ChannelNacl::Close() {
184   // For now, we assume that at shutdown, the reader thread will be woken with
185   // a failure (see NaClIPCAdapter::BlockingRead and CloseChannel). Or... we
186   // might simply be killed with no chance to clean up anyway :-).
187   // If untrusted code tries to close the channel prior to shutdown, it's likely
188   // to hang.
189   // TODO(dmichael): Can we do anything smarter here to make sure the reader
190   //                 thread wakes up and quits?
191   reader_thread_->Join();
192   close(pipe_);
193   pipe_ = -1;
194   reader_thread_runner_.reset();
195   reader_thread_.reset();
196   read_queue_.clear();
197   output_queue_.clear();
198 }
199 
Send(Message * message)200 bool ChannelNacl::Send(Message* message) {
201   DCHECK(!message->HasAttachments());
202   DVLOG(2) << "sending message @" << message << " on channel @" << this
203            << " with type " << message->type();
204   std::unique_ptr<Message> message_ptr(message);
205 
206 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
207   Logging::GetInstance()->OnSendMessage(message_ptr.get());
208 #endif  // BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
209 
210   TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"),
211                          "ChannelNacl::Send",
212                          message->header()->flags,
213                          TRACE_EVENT_FLAG_FLOW_OUT);
214   output_queue_.push_back(std::move(message_ptr));
215   if (!waiting_connect_)
216     return ProcessOutgoingMessages();
217 
218   return true;
219 }
220 
DidRecvMsg(std::unique_ptr<MessageContents> contents)221 void ChannelNacl::DidRecvMsg(std::unique_ptr<MessageContents> contents) {
222   // Close sets the pipe to -1. It's possible we'll get a buffer sent to us from
223   // the reader thread after Close is called. If so, we ignore it.
224   if (pipe_ == -1)
225     return;
226 
227   auto data = std::make_unique<std::vector<char>>();
228   data->swap(contents->data);
229   read_queue_.push_back(std::move(data));
230 
231   input_attachments_.reserve(contents->fds.size());
232   for (int fd : contents->fds) {
233     input_attachments_.push_back(
234         new internal::PlatformFileAttachment(base::ScopedFD(fd)));
235   }
236   contents->fds.clear();
237 
238   // In POSIX, we would be told when there are bytes to read by implementing
239   // OnFileCanReadWithoutBlocking in MessagePumpForIO::FdWatcher. In NaCl, we
240   // instead know at this point because the reader thread posted some data to
241   // us.
242   ProcessIncomingMessages();
243 }
244 
ReadDidFail()245 void ChannelNacl::ReadDidFail() {
246   Close();
247 }
248 
CreatePipe(const IPC::ChannelHandle & channel_handle)249 bool ChannelNacl::CreatePipe(
250     const IPC::ChannelHandle& channel_handle) {
251   DCHECK(pipe_ == -1);
252 
253   // There's one possible case in NaCl:
254   // 1) It's a channel wrapping a pipe that is given to us.
255   // We don't support these:
256   // 2) It's for a named channel.
257   // 3) It's for a client that we implement ourself.
258   // 4) It's the initial IPC channel.
259 
260   if (channel_handle.socket.fd == -1) {
261     NOTIMPLEMENTED();
262     return false;
263   }
264   pipe_ = channel_handle.socket.fd;
265   return true;
266 }
267 
ProcessOutgoingMessages()268 bool ChannelNacl::ProcessOutgoingMessages() {
269   DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
270                               // no connection?
271   if (output_queue_.empty())
272     return true;
273 
274   if (pipe_ == -1)
275     return false;
276 
277   // Write out all the messages. The trusted implementation is guaranteed to not
278   // block. See NaClIPCAdapter::Send for the implementation of imc_sendmsg.
279   while (!output_queue_.empty()) {
280     std::unique_ptr<Message> msg = std::move(output_queue_.front());
281     output_queue_.pop_front();
282 
283     const size_t num_fds = msg->attachment_set()->size();
284     DCHECK(num_fds <= MessageAttachmentSet::kMaxDescriptorsPerMessage);
285     std::vector<int> fds;
286     fds.reserve(num_fds);
287     for (size_t i = 0; i < num_fds; i++) {
288       scoped_refptr<MessageAttachment> attachment =
289           msg->attachment_set()->GetAttachmentAt(i);
290       DCHECK_EQ(MessageAttachment::Type::PLATFORM_FILE, attachment->GetType());
291       fds.push_back(static_cast<internal::PlatformFileAttachment&>(*attachment)
292                         .TakePlatformFile());
293     }
294 
295     NaClAbiNaClImcMsgIoVec iov = {
296       const_cast<void*>(msg->data()), msg->size()
297     };
298     NaClAbiNaClImcMsgHdr msgh = {&iov, 1, fds.data(), num_fds};
299     ssize_t bytes_written = imc_sendmsg(pipe_, &msgh, 0);
300 
301     DCHECK(bytes_written);  // The trusted side shouldn't return 0.
302     if (bytes_written < 0) {
303       // The trusted side should only ever give us an error of EPIPE. We
304       // should never be interrupted, nor should we get EAGAIN.
305       DCHECK(errno == EPIPE);
306       Close();
307       PLOG(ERROR) << "pipe_ error on "
308                   << pipe_
309                   << " Currently writing message of size: "
310                   << msg->size();
311       return false;
312     } else {
313       msg->attachment_set()->CommitAllDescriptors();
314     }
315 
316     // Message sent OK!
317     DVLOG(2) << "sent message @" << msg.get() << " with type " << msg->type()
318              << " on fd " << pipe_;
319   }
320   return true;
321 }
322 
CallOnChannelConnected()323 void ChannelNacl::CallOnChannelConnected() {
324   listener()->OnChannelConnected(-1);
325 }
326 
ReadData(char * buffer,int buffer_len,int * bytes_read)327 ChannelNacl::ReadState ChannelNacl::ReadData(
328     char* buffer,
329     int buffer_len,
330     int* bytes_read) {
331   *bytes_read = 0;
332   if (pipe_ == -1)
333     return READ_FAILED;
334   if (read_queue_.empty())
335     return READ_PENDING;
336   while (!read_queue_.empty() && *bytes_read < buffer_len) {
337     std::vector<char>* vec = read_queue_.front().get();
338     size_t bytes_to_read = buffer_len - *bytes_read;
339     if (vec->size() <= bytes_to_read) {
340       // We can read and discard the entire vector.
341       std::copy(vec->begin(), vec->end(), buffer + *bytes_read);
342       *bytes_read += vec->size();
343       read_queue_.pop_front();
344     } else {
345       // Read all the bytes we can and discard them from the front of the
346       // vector. (This can be slowish, since erase has to move the back of the
347       // vector to the front, but it's hopefully a temporary hack and it keeps
348       // the code simple).
349       std::copy(vec->begin(), vec->begin() + bytes_to_read,
350                 buffer + *bytes_read);
351       vec->erase(vec->begin(), vec->begin() + bytes_to_read);
352       *bytes_read += bytes_to_read;
353     }
354   }
355   return READ_SUCCEEDED;
356 }
357 
ShouldDispatchInputMessage(Message * msg)358 bool ChannelNacl::ShouldDispatchInputMessage(Message* msg) {
359   return true;
360 }
361 
GetAttachments(Message * msg)362 bool ChannelNacl::GetAttachments(Message* msg) {
363   uint16_t header_fds = msg->header()->num_fds;
364   CHECK(header_fds == input_attachments_.size());
365   if (header_fds == 0)
366     return true;  // Nothing to do.
367 
368   for (auto& attachment : input_attachments_) {
369     msg->attachment_set()->AddAttachment(std::move(attachment));
370   }
371   input_attachments_.clear();
372   return true;
373 }
374 
DidEmptyInputBuffers()375 bool ChannelNacl::DidEmptyInputBuffers() {
376   // When the input data buffer is empty, the attachments should be too.
377   return input_attachments_.empty();
378 }
379 
HandleInternalMessage(const Message & msg)380 void ChannelNacl::HandleInternalMessage(const Message& msg) {
381   // The trusted side IPC::Channel should handle the "hello" handshake; we
382   // should not receive the "Hello" message.
383   NOTREACHED();
384 }
385 
386 // Channel's methods
387 
388 // static
Create(const IPC::ChannelHandle & channel_handle,Mode mode,Listener * listener)389 std::unique_ptr<Channel> Channel::Create(
390     const IPC::ChannelHandle& channel_handle,
391     Mode mode,
392     Listener* listener) {
393   return std::make_unique<ChannelNacl>(channel_handle, mode, listener);
394 }
395 
396 }  // namespace IPC
397