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