1// Copyright (c) 2013 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 "ppapi/proxy/handle_converter.h"
6
7#include <vector>
8#include "base/bind.h"
9#include "ipc/ipc_message.h"
10#include "ipc/ipc_message_macros.h"
11#include "ppapi/proxy/ppapi_messages.h"
12#include "ppapi/proxy/resource_message_params.h"
13#include "ppapi/proxy/serialized_handle.h"
14#include "ppapi/proxy/serialized_var.h"
15
16class NaClDescImcShm;
17
18namespace IPC {
19class Message;
20}
21
22namespace {
23
24void WriteHandle(int handle_index,
25                 const ppapi::proxy::SerializedHandle& handle,
26                 IPC::Message* message) {
27  ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), message);
28
29  // Now write the handle itself in POSIX style.
30  message->WriteBool(true);  // valid == true
31  message->WriteInt(handle_index);
32}
33
34typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
35
36// We define overloads for catching SerializedHandles so that we can share
37// them correctly to the untrusted side.
38// See ConvertHandlesImpl for how these get used.
39void ConvertHandlesInParam(const ppapi::proxy::SerializedHandle& handle,
40                           Handles* handles,
41                           IPC::Message* msg,
42                           int* handle_index) {
43  handles->push_back(handle);
44  if (msg)
45    WriteHandle((*handle_index)++, handle, msg);
46}
47
48void HandleWriter(int* handle_index,
49                  IPC::Message* m,
50                  const ppapi::proxy::SerializedHandle& handle) {
51  WriteHandle((*handle_index)++, handle, m);
52}
53
54void ConvertHandlesInParam(const ppapi::proxy::SerializedVar& var,
55                           Handles* handles,
56                           IPC::Message* msg,
57                           int* handle_index) {
58  std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles();
59  if (var_handles.empty())
60    return;
61
62  for (size_t i = 0; i < var_handles.size(); ++i)
63    handles->push_back(*var_handles[i]);
64  if (msg)
65    var.WriteDataToMessage(msg, base::Bind(&HandleWriter, handle_index));
66}
67
68// For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
69// the handles are carried inside the ResourceMessageReplyParams.
70// NOTE: We only translate handles from host->NaCl. The only kind of
71//       ResourceMessageParams that travels this direction is
72//       ResourceMessageReplyParams, so that's the only one we need to handle.
73void ConvertHandlesInParam(
74    const ppapi::proxy::ResourceMessageReplyParams& params,
75    Handles* handles,
76    IPC::Message* msg,
77    int* handle_index) {
78  // First, if we need to rewrite the message parameters, write everything
79  // before the handles (there's nothing after the handles).
80  if (msg) {
81    params.WriteReplyHeader(msg);
82    // IPC writes the vector length as an int before the contents of the
83    // vector.
84    msg->WriteInt(static_cast<int>(params.handles().size()));
85  }
86  for (Handles::const_iterator iter = params.handles().begin();
87       iter != params.handles().end();
88       ++iter) {
89    // ConvertHandle will write each handle to |msg|, if necessary.
90    ConvertHandlesInParam(*iter, handles, msg, handle_index);
91  }
92  // Tell ResourceMessageReplyParams that we have taken the handles, so it
93  // shouldn't close them. The NaCl runtime will take ownership of them.
94  params.ConsumeHandles();
95}
96
97// This overload is to catch all types other than SerializedHandle or
98// ResourceMessageReplyParams. On Windows, |msg| will be a valid pointer, and we
99// must write |param| to it.
100template <class T>
101void ConvertHandlesInParam(const T& param,
102                           Handles* /* handles */,
103                           IPC::Message* msg,
104                           int* /* handle_index */) {
105  // It's not a handle, so just write to the output message, if necessary.
106  if (msg)
107    IPC::WriteParam(msg, param);
108}
109
110// These just break apart the given tuple and run ConvertHandle over each param.
111// The idea is to extract any handles in the tuple, while writing all data to
112// msg (if msg is valid). The msg will only be valid on Windows, where we need
113// to re-write all of the message parameters, writing the handles in POSIX style
114// for NaCl.
115template <class A>
116void ConvertHandlesImpl(const Tuple1<A>& t1, Handles* handles,
117                        IPC::Message* msg) {
118  int handle_index = 0;
119  ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
120}
121template <class A, class B>
122void ConvertHandlesImpl(const Tuple2<A, B>& t1, Handles* handles,
123                        IPC::Message* msg) {
124  int handle_index = 0;
125  ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
126  ConvertHandlesInParam(t1.b, handles, msg, &handle_index);
127}
128template <class A, class B, class C>
129void ConvertHandlesImpl(const Tuple3<A, B, C>& t1, Handles* handles,
130                        IPC::Message* msg) {
131  int handle_index = 0;
132  ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
133  ConvertHandlesInParam(t1.b, handles, msg, &handle_index);
134  ConvertHandlesInParam(t1.c, handles, msg, &handle_index);
135}
136template <class A, class B, class C, class D>
137void ConvertHandlesImpl(const Tuple4<A, B, C, D>& t1, Handles* handles,
138                        IPC::Message* msg) {
139  int handle_index = 0;
140  ConvertHandlesInParam(t1.a, handles, msg, &handle_index);
141  ConvertHandlesInParam(t1.b, handles, msg, &handle_index);
142  ConvertHandlesInParam(t1.c, handles, msg, &handle_index);
143  ConvertHandlesInParam(t1.d, handles, msg, &handle_index);
144}
145
146template <class MessageType>
147class HandleConverterImpl {
148 public:
149  explicit HandleConverterImpl(const IPC::Message* msg)
150      : msg_(static_cast<const MessageType*>(msg)) {
151  }
152  bool ConvertMessage(Handles* handles, IPC::Message* out_msg) {
153    typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params;
154    if (!MessageType::Read(msg_, &params))
155      return false;
156    ConvertHandlesImpl(params, handles, out_msg);
157    return true;
158  }
159
160  bool ConvertReply(Handles* handles, IPC::SyncMessage* out_msg) {
161    typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple
162        params;
163    if (!MessageType::ReadReplyParam(msg_, &params))
164      return false;
165    // If we need to rewrite the message (i.e., on Windows), we need to make
166    // sure we write the message id first.
167    if (out_msg) {
168      out_msg->set_reply();
169      int id = IPC::SyncMessage::GetMessageId(*msg_);
170      out_msg->WriteInt(id);
171    }
172    ConvertHandlesImpl(params, handles, out_msg);
173    return true;
174  }
175  // TODO(dmichael): Add ConvertSyncMessage for outgoing sync messages, if we
176  //                 ever pass handles in one of those.
177
178 private:
179  const MessageType* msg_;
180};
181
182}  // namespace
183
184#define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
185      case MESSAGE_TYPE::ID: { \
186        HandleConverterImpl<MESSAGE_TYPE> extractor(&msg); \
187        if (!extractor.ConvertMessage(handles, new_msg_ptr->get())) \
188          return false; \
189        break; \
190      }
191#define CASE_FOR_REPLY(MESSAGE_TYPE) \
192      case MESSAGE_TYPE::ID: { \
193        HandleConverterImpl<MESSAGE_TYPE> extractor(&msg); \
194        if (!extractor.ConvertReply( \
195                handles, \
196                static_cast<IPC::SyncMessage*>(new_msg_ptr->get()))) \
197          return false; \
198        break; \
199      }
200
201namespace ppapi {
202namespace proxy {
203
204class SerializedHandle;
205
206HandleConverter::HandleConverter() {
207}
208
209bool HandleConverter::ConvertNativeHandlesToPosix(
210    const IPC::Message& msg,
211    std::vector<SerializedHandle>* handles,
212    scoped_ptr<IPC::Message>* new_msg_ptr) {
213  DCHECK(handles);
214  DCHECK(new_msg_ptr);
215  DCHECK(!new_msg_ptr->get());
216
217  // In Windows, we need to re-write the contents of the message. This is
218  // because in Windows IPC code, native HANDLE values are serialized in the
219  // body of the message.
220  //
221  // In POSIX, we only serialize an index in to a FileDescriptorSet, and the
222  // actual file descriptors are sent out-of-band. So on Windows, to make a
223  // message that's compatible with Windows, we need to write a new message that
224  // has simple indices in the message body instead of the HANDLEs.
225  //
226  // NOTE: This means on Windows, new_msg_ptr's serialized contents are not
227  // compatible with Windows IPC deserialization code; it is intended to be
228  // passed to NaCl.
229#if defined(OS_WIN)
230  new_msg_ptr->reset(
231      new IPC::Message(msg.routing_id(), msg.type(), msg.priority()));
232#else
233  // Even on POSIX, we have to rewrite messages to create channels, because
234  // these contain a handle with an invalid (place holder) descriptor. The
235  // message sending code sees this and doesn't pass the descriptor over
236  // correctly.
237  if (msg.type() == PpapiMsg_CreateNaClChannel::ID) {
238    new_msg_ptr->reset(
239        new IPC::Message(msg.routing_id(), msg.type(), msg.priority()));
240  }
241#endif
242
243  switch (msg.type()) {
244    CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel)
245    CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
246    CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
247    CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
248    case IPC_REPLY_ID: {
249      int id = IPC::SyncMessage::GetMessageId(msg);
250      PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id));
251      if (iter == pending_sync_msgs_.end()) {
252        NOTREACHED();
253        return false;
254      }
255      uint32_t type = iter->second;
256      pending_sync_msgs_.erase(iter);
257      switch (type) {
258        CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer)
259        CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple)
260        CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall)
261        CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory)
262        default:
263          // Do nothing for messages we don't know.
264          break;
265      }
266      break;
267    }
268    default:
269      // Do nothing for messages we don't know.
270      break;
271  }
272  return true;
273}
274
275void HandleConverter::RegisterSyncMessageForReply(const IPC::Message& msg) {
276  DCHECK(msg.is_sync());
277
278  int msg_id = IPC::SyncMessage::GetMessageId(msg);
279  DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end());
280
281  pending_sync_msgs_[msg_id] = msg.type();
282}
283
284}  // namespace proxy
285}  // namespace ppapi
286