[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 1 | // Copyright 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/nacl_message_scanner.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 | |
| 16 | class NaClDescImcShm; |
| 17 | |
| 18 | namespace IPC { |
| 19 | class Message; |
| 20 | } |
| 21 | |
| 22 | namespace { |
| 23 | |
| 24 | typedef std::vector<ppapi::proxy::SerializedHandle> Handles; |
| 25 | |
| 26 | struct ScanningResults { |
| 27 | ScanningResults() : handle_index(0) {} |
| 28 | |
| 29 | // Vector to hold handles found in the message. |
| 30 | Handles handles; |
| 31 | // Current handle index in the rewritten message. During the scan, it will be |
| 32 | // be less than or equal to handles.size(). After the scan it should be equal. |
| 33 | int handle_index; |
| 34 | // The rewritten message. This may be NULL, so all ScanParam overloads should |
[email protected] | e74d2d1 | 2013-11-02 16:17:37 | [diff] [blame] | 35 | // check for NULL before writing to it. In some cases, a ScanParam overload |
| 36 | // may set this to NULL when it can determine that there are no parameters |
| 37 | // that need conversion. (See the ResourceMessageReplyParams overload.) |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 38 | scoped_ptr<IPC::Message> new_msg; |
| 39 | }; |
| 40 | |
| 41 | void WriteHandle(int handle_index, |
| 42 | const ppapi::proxy::SerializedHandle& handle, |
| 43 | IPC::Message* msg) { |
| 44 | ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), msg); |
| 45 | |
| 46 | // Now write the handle itself in POSIX style. |
| 47 | msg->WriteBool(true); // valid == true |
| 48 | msg->WriteInt(handle_index); |
| 49 | } |
| 50 | |
| 51 | // Define overloads for each kind of message parameter that requires special |
| 52 | // handling. See ScanTuple for how these get used. |
| 53 | |
| 54 | // Overload to match SerializedHandle. |
| 55 | void ScanParam(const ppapi::proxy::SerializedHandle& handle, |
| 56 | ScanningResults* results) { |
| 57 | results->handles.push_back(handle); |
| 58 | if (results->new_msg) |
| 59 | WriteHandle(results->handle_index++, handle, results->new_msg.get()); |
| 60 | } |
| 61 | |
| 62 | void HandleWriter(int* handle_index, |
| 63 | IPC::Message* m, |
| 64 | const ppapi::proxy::SerializedHandle& handle) { |
| 65 | WriteHandle((*handle_index)++, handle, m); |
| 66 | } |
| 67 | |
| 68 | // Overload to match SerializedVar, which can contain handles. |
| 69 | void ScanParam(const ppapi::proxy::SerializedVar& var, |
| 70 | ScanningResults* results) { |
| 71 | std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles(); |
[email protected] | e74d2d1 | 2013-11-02 16:17:37 | [diff] [blame] | 72 | // Copy any handles and then rewrite the message. |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 73 | for (size_t i = 0; i < var_handles.size(); ++i) |
| 74 | results->handles.push_back(*var_handles[i]); |
| 75 | if (results->new_msg) |
| 76 | var.WriteDataToMessage(results->new_msg.get(), |
| 77 | base::Bind(&HandleWriter, &results->handle_index)); |
| 78 | } |
| 79 | |
| 80 | // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, |
| 81 | // the handles are carried inside the ResourceMessageReplyParams. |
| 82 | // NOTE: We only intercept handles from host->NaCl. The only kind of |
| 83 | // ResourceMessageParams that travels this direction is |
| 84 | // ResourceMessageReplyParams, so that's the only one we need to handle. |
| 85 | void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params, |
| 86 | ScanningResults* results) { |
[email protected] | e74d2d1 | 2013-11-02 16:17:37 | [diff] [blame] | 87 | // If the resource reply params don't contain handles, NULL the new message |
| 88 | // pointer to cancel further rewriting. |
| 89 | // NOTE: This works because only handles currently need rewriting, and we |
| 90 | // know at this point that this message has none. |
| 91 | if (params.handles().empty()) { |
| 92 | results->new_msg.reset(NULL); |
| 93 | return; |
| 94 | } |
| 95 | |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 96 | // If we need to rewrite the message, write everything before the handles |
| 97 | // (there's nothing after the handles). |
| 98 | if (results->new_msg) { |
| 99 | params.WriteReplyHeader(results->new_msg.get()); |
| 100 | // IPC writes the vector length as an int before the contents of the |
| 101 | // vector. |
| 102 | results->new_msg->WriteInt(static_cast<int>(params.handles().size())); |
| 103 | } |
| 104 | for (Handles::const_iterator iter = params.handles().begin(); |
| 105 | iter != params.handles().end(); |
| 106 | ++iter) { |
| 107 | // ScanParam will write each handle to the new message, if necessary. |
| 108 | ScanParam(*iter, results); |
| 109 | } |
| 110 | // Tell ResourceMessageReplyParams that we have taken the handles, so it |
| 111 | // shouldn't close them. The NaCl runtime will take ownership of them. |
| 112 | params.ConsumeHandles(); |
| 113 | } |
| 114 | |
| 115 | // Overload to match all other types. If we need to rewrite the message, |
| 116 | // write the parameter. |
| 117 | template <class T> |
| 118 | void ScanParam(const T& param, ScanningResults* results) { |
| 119 | if (results->new_msg) |
| 120 | IPC::WriteParam(results->new_msg.get(), param); |
| 121 | } |
| 122 | |
| 123 | // These just break apart the given tuple and run ScanParam over each param. |
| 124 | // The idea is to scan elements in the tuple which require special handling, |
| 125 | // and write them into the |results| struct. |
| 126 | template <class A> |
| 127 | void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) { |
| 128 | ScanParam(t1.a, results); |
| 129 | } |
| 130 | template <class A, class B> |
| 131 | void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) { |
| 132 | ScanParam(t1.a, results); |
| 133 | ScanParam(t1.b, results); |
| 134 | } |
| 135 | template <class A, class B, class C> |
| 136 | void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) { |
| 137 | ScanParam(t1.a, results); |
| 138 | ScanParam(t1.b, results); |
| 139 | ScanParam(t1.c, results); |
| 140 | } |
| 141 | template <class A, class B, class C, class D> |
| 142 | void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) { |
| 143 | ScanParam(t1.a, results); |
| 144 | ScanParam(t1.b, results); |
| 145 | ScanParam(t1.c, results); |
| 146 | ScanParam(t1.d, results); |
| 147 | } |
| 148 | |
| 149 | template <class MessageType> |
| 150 | class MessageScannerImpl { |
| 151 | public: |
| 152 | explicit MessageScannerImpl(const IPC::Message* msg) |
| 153 | : msg_(static_cast<const MessageType*>(msg)) { |
| 154 | } |
| 155 | bool ScanMessage(ScanningResults* results) { |
| 156 | typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params; |
| 157 | if (!MessageType::Read(msg_, ¶ms)) |
| 158 | return false; |
| 159 | ScanTuple(params, results); |
| 160 | return true; |
| 161 | } |
| 162 | |
| 163 | bool ScanReply(ScanningResults* results) { |
| 164 | typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple |
| 165 | params; |
| 166 | if (!MessageType::ReadReplyParam(msg_, ¶ms)) |
| 167 | return false; |
| 168 | // If we need to rewrite the message, write the message id first. |
| 169 | if (results->new_msg) { |
| 170 | results->new_msg->set_reply(); |
| 171 | int id = IPC::SyncMessage::GetMessageId(*msg_); |
| 172 | results->new_msg->WriteInt(id); |
| 173 | } |
| 174 | ScanTuple(params, results); |
| 175 | return true; |
| 176 | } |
| 177 | // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever |
| 178 | // need to scan those. |
| 179 | |
| 180 | private: |
| 181 | const MessageType* msg_; |
| 182 | }; |
| 183 | |
| 184 | } // namespace |
| 185 | |
| 186 | #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \ |
| 187 | case MESSAGE_TYPE::ID: { \ |
| 188 | MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ |
| 189 | if (rewrite_msg) \ |
| 190 | results.new_msg.reset( \ |
[email protected] | 753bb25 | 2013-11-04 22:28:12 | [diff] [blame] | 191 | new IPC::Message(msg.routing_id(), msg.type(), \ |
| 192 | IPC::Message::PRIORITY_NORMAL)); \ |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 193 | if (!scanner.ScanMessage(&results)) \ |
| 194 | return false; \ |
| 195 | break; \ |
| 196 | } |
| 197 | #define CASE_FOR_REPLY(MESSAGE_TYPE) \ |
| 198 | case MESSAGE_TYPE::ID: { \ |
| 199 | MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ |
| 200 | if (rewrite_msg) \ |
| 201 | results.new_msg.reset( \ |
[email protected] | 753bb25 | 2013-11-04 22:28:12 | [diff] [blame] | 202 | new IPC::Message(msg.routing_id(), msg.type(), \ |
| 203 | IPC::Message::PRIORITY_NORMAL)); \ |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 204 | if (!scanner.ScanReply(&results)) \ |
| 205 | return false; \ |
| 206 | break; \ |
| 207 | } |
| 208 | |
| 209 | namespace ppapi { |
| 210 | namespace proxy { |
| 211 | |
| 212 | class SerializedHandle; |
| 213 | |
| 214 | NaClMessageScanner::NaClMessageScanner() { |
| 215 | } |
| 216 | |
| 217 | // Windows IPC differs from POSIX in that native handles are serialized in the |
| 218 | // message body, rather than passed in a separate FileDescriptorSet. Therefore, |
| 219 | // on Windows, any message containing handles must be rewritten in the POSIX |
| 220 | // format before we can send it to the NaCl plugin. |
| 221 | // |
| 222 | // On POSIX and Windows we have to rewrite PpapiMsg_CreateNaClChannel messages. |
| 223 | // These contain a handle with an invalid (place holder) descriptor. We need to |
| 224 | // locate this handle so it can be replaced with a valid one when the channel is |
| 225 | // created. |
| 226 | bool NaClMessageScanner::ScanMessage( |
| 227 | const IPC::Message& msg, |
| 228 | std::vector<SerializedHandle>* handles, |
| 229 | scoped_ptr<IPC::Message>* new_msg_ptr) { |
| 230 | DCHECK(handles); |
| 231 | DCHECK(handles->empty()); |
| 232 | DCHECK(new_msg_ptr); |
| 233 | DCHECK(!new_msg_ptr->get()); |
| 234 | |
| 235 | bool rewrite_msg = |
| 236 | #if defined(OS_WIN) |
| 237 | true; |
| 238 | #else |
| 239 | (msg.type() == PpapiMsg_CreateNaClChannel::ID); |
| 240 | #endif |
| 241 | |
[email protected] | e74d2d1 | 2013-11-02 16:17:37 | [diff] [blame] | 242 | |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 243 | // We can't always tell from the message ID if rewriting is needed. Therefore, |
[email protected] | e74d2d1 | 2013-11-02 16:17:37 | [diff] [blame] | 244 | // scan any message types that might contain a handle. If we later determine |
| 245 | // that there are no handles, we can cancel the rewriting by clearing the |
| 246 | // results.new_msg pointer. |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 247 | ScanningResults results; |
| 248 | switch (msg.type()) { |
| 249 | CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel) |
| 250 | CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated) |
| 251 | CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage) |
| 252 | CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply) |
| 253 | case IPC_REPLY_ID: { |
| 254 | int id = IPC::SyncMessage::GetMessageId(msg); |
| 255 | PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id)); |
| 256 | if (iter == pending_sync_msgs_.end()) { |
| 257 | NOTREACHED(); |
| 258 | return false; |
| 259 | } |
| 260 | uint32_t type = iter->second; |
| 261 | pending_sync_msgs_.erase(iter); |
| 262 | switch (type) { |
| 263 | CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer) |
| 264 | CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple) |
| 265 | CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall) |
| 266 | CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory) |
| 267 | default: |
| 268 | // Do nothing for messages we don't know. |
| 269 | break; |
| 270 | } |
| 271 | break; |
| 272 | } |
| 273 | default: |
| 274 | // Do nothing for messages we don't know. |
| 275 | break; |
| 276 | } |
| 277 | |
| 278 | // Only messages containing handles need to be rewritten. If no handles are |
| 279 | // found, don't return the rewritten message either. This must be changed if |
| 280 | // we ever add new param types that also require rewriting. |
| 281 | if (!results.handles.empty()) { |
| 282 | handles->swap(results.handles); |
| 283 | *new_msg_ptr = results.new_msg.Pass(); |
| 284 | } |
| 285 | return true; |
| 286 | } |
| 287 | |
[email protected] | c586216c | 2013-12-18 21:59:17 | [diff] [blame^] | 288 | void NaClMessageScanner::ScanUntrustedMessage( |
| 289 | const IPC::Message& untrusted_msg, |
| 290 | scoped_ptr<IPC::Message>* new_msg_ptr) { |
| 291 | if (untrusted_msg.is_sync()) |
| 292 | RegisterSyncMessageForReply(untrusted_msg); |
| 293 | // TODO(bbudge) Add message auditing for FileSystem and FileIO resources when |
| 294 | // we implement Write on the plugin side of the proxy. See crbug.com/194304. |
| 295 | } |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 296 | |
[email protected] | c586216c | 2013-12-18 21:59:17 | [diff] [blame^] | 297 | void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) { |
[email protected] | 6276b49 | 2013-11-02 13:38:31 | [diff] [blame] | 298 | int msg_id = IPC::SyncMessage::GetMessageId(msg); |
| 299 | DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end()); |
| 300 | |
| 301 | pending_sync_msgs_[msg_id] = msg.type(); |
| 302 | } |
| 303 | |
| 304 | } // namespace proxy |
| 305 | } // namespace ppapi |