// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ppapi/proxy/nacl_message_scanner.h" #include <vector> #include "base/bind.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/resource_message_params.h" #include "ppapi/proxy/serialized_handle.h" #include "ppapi/proxy/serialized_var.h" class NaClDescImcShm; namespace IPC { class Message; } namespace { typedef std::vector<ppapi::proxy::SerializedHandle> Handles; struct ScanningResults { ScanningResults() : handle_index(0) {} // Vector to hold handles found in the message. Handles handles; // Current handle index in the rewritten message. During the scan, it will be // be less than or equal to handles.size(). After the scan it should be equal. int handle_index; // The rewritten message. This may be NULL, so all ScanParam overloads should // check for NULL before writing to it. In some cases, a ScanParam overload // may set this to NULL when it can determine that there are no parameters // that need conversion. (See the ResourceMessageReplyParams overload.) scoped_ptr<IPC::Message> new_msg; }; void WriteHandle(int handle_index, const ppapi::proxy::SerializedHandle& handle, IPC::Message* msg) { ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), msg); // Now write the handle itself in POSIX style. msg->WriteBool(true); // valid == true msg->WriteInt(handle_index); } // Define overloads for each kind of message parameter that requires special // handling. See ScanTuple for how these get used. // Overload to match SerializedHandle. void ScanParam(const ppapi::proxy::SerializedHandle& handle, ScanningResults* results) { results->handles.push_back(handle); if (results->new_msg) WriteHandle(results->handle_index++, handle, results->new_msg.get()); } void HandleWriter(int* handle_index, IPC::Message* m, const ppapi::proxy::SerializedHandle& handle) { WriteHandle((*handle_index)++, handle, m); } // Overload to match SerializedVar, which can contain handles. void ScanParam(const ppapi::proxy::SerializedVar& var, ScanningResults* results) { std::vector<ppapi::proxy::SerializedHandle*> var_handles = var.GetHandles(); // Copy any handles and then rewrite the message. for (size_t i = 0; i < var_handles.size(); ++i) results->handles.push_back(*var_handles[i]); if (results->new_msg) var.WriteDataToMessage(results->new_msg.get(), base::Bind(&HandleWriter, &results->handle_index)); } // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, // the handles are carried inside the ResourceMessageReplyParams. // NOTE: We only intercept handles from host->NaCl. The only kind of // ResourceMessageParams that travels this direction is // ResourceMessageReplyParams, so that's the only one we need to handle. void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params, ScanningResults* results) { // If the resource reply params don't contain handles, NULL the new message // pointer to cancel further rewriting. // NOTE: This works because only handles currently need rewriting, and we // know at this point that this message has none. if (params.handles().empty()) { results->new_msg.reset(NULL); return; } // If we need to rewrite the message, write everything before the handles // (there's nothing after the handles). if (results->new_msg) { params.WriteReplyHeader(results->new_msg.get()); // IPC writes the vector length as an int before the contents of the // vector. results->new_msg->WriteInt(static_cast<int>(params.handles().size())); } for (Handles::const_iterator iter = params.handles().begin(); iter != params.handles().end(); ++iter) { // ScanParam will write each handle to the new message, if necessary. ScanParam(*iter, results); } // Tell ResourceMessageReplyParams that we have taken the handles, so it // shouldn't close them. The NaCl runtime will take ownership of them. params.ConsumeHandles(); } // Overload to match all other types. If we need to rewrite the message, // write the parameter. template <class T> void ScanParam(const T& param, ScanningResults* results) { if (results->new_msg) IPC::WriteParam(results->new_msg.get(), param); } // These just break apart the given tuple and run ScanParam over each param. // The idea is to scan elements in the tuple which require special handling, // and write them into the |results| struct. template <class A> void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) { ScanParam(t1.a, results); } template <class A, class B> void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) { ScanParam(t1.a, results); ScanParam(t1.b, results); } template <class A, class B, class C> void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) { ScanParam(t1.a, results); ScanParam(t1.b, results); ScanParam(t1.c, results); } template <class A, class B, class C, class D> void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) { ScanParam(t1.a, results); ScanParam(t1.b, results); ScanParam(t1.c, results); ScanParam(t1.d, results); } template <class MessageType> class MessageScannerImpl { public: explicit MessageScannerImpl(const IPC::Message* msg) : msg_(static_cast<const MessageType*>(msg)) { } bool ScanMessage(ScanningResults* results) { typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params; if (!MessageType::Read(msg_, ¶ms)) return false; ScanTuple(params, results); return true; } bool ScanReply(ScanningResults* results) { typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple params; if (!MessageType::ReadReplyParam(msg_, ¶ms)) return false; // If we need to rewrite the message, write the message id first. if (results->new_msg) { results->new_msg->set_reply(); int id = IPC::SyncMessage::GetMessageId(*msg_); results->new_msg->WriteInt(id); } ScanTuple(params, results); return true; } // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever // need to scan those. private: const MessageType* msg_; }; } // namespace #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \ case MESSAGE_TYPE::ID: { \ MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ if (rewrite_msg) \ results.new_msg.reset( \ new IPC::Message(msg.routing_id(), msg.type(), \ IPC::Message::PRIORITY_NORMAL)); \ if (!scanner.ScanMessage(&results)) \ return false; \ break; \ } #define CASE_FOR_REPLY(MESSAGE_TYPE) \ case MESSAGE_TYPE::ID: { \ MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \ if (rewrite_msg) \ results.new_msg.reset( \ new IPC::Message(msg.routing_id(), msg.type(), \ IPC::Message::PRIORITY_NORMAL)); \ if (!scanner.ScanReply(&results)) \ return false; \ break; \ } namespace ppapi { namespace proxy { class SerializedHandle; NaClMessageScanner::NaClMessageScanner() { } // Windows IPC differs from POSIX in that native handles are serialized in the // message body, rather than passed in a separate FileDescriptorSet. Therefore, // on Windows, any message containing handles must be rewritten in the POSIX // format before we can send it to the NaCl plugin. // // On POSIX and Windows we have to rewrite PpapiMsg_CreateNaClChannel messages. // These contain a handle with an invalid (place holder) descriptor. We need to // locate this handle so it can be replaced with a valid one when the channel is // created. bool NaClMessageScanner::ScanMessage( const IPC::Message& msg, std::vector<SerializedHandle>* handles, scoped_ptr<IPC::Message>* new_msg_ptr) { DCHECK(handles); DCHECK(handles->empty()); DCHECK(new_msg_ptr); DCHECK(!new_msg_ptr->get()); bool rewrite_msg = #if defined(OS_WIN) true; #else (msg.type() == PpapiMsg_CreateNaClChannel::ID); #endif // We can't always tell from the message ID if rewriting is needed. Therefore, // scan any message types that might contain a handle. If we later determine // that there are no handles, we can cancel the rewriting by clearing the // results.new_msg pointer. ScanningResults results; switch (msg.type()) { CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel) CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated) CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage) CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply) case IPC_REPLY_ID: { int id = IPC::SyncMessage::GetMessageId(msg); PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id)); if (iter == pending_sync_msgs_.end()) { NOTREACHED(); return false; } uint32_t type = iter->second; pending_sync_msgs_.erase(iter); switch (type) { CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer) CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple) CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall) CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory) default: // Do nothing for messages we don't know. break; } break; } default: // Do nothing for messages we don't know. break; } // Only messages containing handles need to be rewritten. If no handles are // found, don't return the rewritten message either. This must be changed if // we ever add new param types that also require rewriting. if (!results.handles.empty()) { handles->swap(results.handles); *new_msg_ptr = results.new_msg.Pass(); } return true; } void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) { DCHECK(msg.is_sync()); int msg_id = IPC::SyncMessage::GetMessageId(msg); DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end()); pending_sync_msgs_[msg_id] = msg.type(); } } // namespace proxy } // namespace ppapi