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