1 // Copyright 2013 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 #ifndef COMPONENTS_NACL_LOADER_NACL_IPC_ADAPTER_H_ 6 #define COMPONENTS_NACL_LOADER_NACL_IPC_ADAPTER_H_ 7 8 #include <stddef.h> 9 10 #include <map> 11 #include <memory> 12 #include <string> 13 14 #include "base/containers/queue.h" 15 #include "base/files/scoped_file.h" 16 #include "base/functional/callback.h" 17 #include "base/memory/ref_counted.h" 18 #include "base/pickle.h" 19 #include "base/synchronization/condition_variable.h" 20 #include "base/synchronization/lock.h" 21 #include "base/task/task_runner.h" 22 #include "build/build_config.h" 23 #include "ipc/ipc_listener.h" 24 #include "ipc/ipc_platform_file.h" 25 #include "ppapi/c/pp_stdint.h" 26 #include "ppapi/proxy/nacl_message_scanner.h" 27 28 struct NaClDesc; 29 struct NaClImcTypedMsgHdr; 30 31 namespace base { 32 class SingleThreadTaskRunner; 33 } 34 35 namespace IPC { 36 class Channel; 37 struct ChannelHandle; 38 } 39 40 // Adapts a Chrome IPC channel to an IPC channel that we expose to Native 41 // Client. This provides a mapping in both directions, so when IPC messages 42 // come in from another process, we rewrite them and allow them to be received 43 // via a recvmsg-like interface in the NaCl code. When NaCl code calls sendmsg, 44 // we implement that as sending IPC messages on the channel. 45 // 46 // This object also provides the necessary logic for rewriting IPC messages. 47 // NaCl code is platform-independent and runs in a Posix-like enviroment, but 48 // some formatting in the message and the way handles are transferred varies 49 // by platform. This class bridges that gap to provide what looks like a 50 // normal platform-specific IPC implementation to Chrome, and a Posix-like 51 // version on every platform to NaCl. 52 // 53 // This object must be threadsafe since the nacl environment determines which 54 // thread every function is called on. 55 class NaClIPCAdapter : public base::RefCountedThreadSafe<NaClIPCAdapter>, 56 public IPC::Listener { 57 public: 58 // Chrome's IPC message format varies by platform, NaCl's does not. In 59 // particular, the header has some extra fields on Posix platforms. Since 60 // NaCl is a Posix environment, it gets that version of the header. This 61 // header is duplicated here so we have a cross-platform definition of the 62 // header we're exposing to NaCl. 63 #pragma pack(push, 4) 64 struct NaClMessageHeader : public base::Pickle::Header { 65 int32_t routing; 66 uint32_t type; 67 uint32_t flags; 68 uint16_t num_fds; 69 uint16_t pad; 70 }; 71 #pragma pack(pop) 72 73 typedef base::OnceCallback<void(IPC::PlatformFileForTransit, base::FilePath)> 74 ResolveFileTokenReplyCallback; 75 76 typedef base::RepeatingCallback<void(uint64_t, // file_token_lo 77 uint64_t, // file_token_hi 78 ResolveFileTokenReplyCallback)> 79 ResolveFileTokenCallback; 80 81 typedef base::OnceCallback< 82 void(const IPC::Message&, IPC::PlatformFileForTransit, base::FilePath)> 83 OpenResourceReplyCallback; 84 85 typedef base::RepeatingCallback<bool(const IPC::Message&, 86 const std::string&, // key 87 OpenResourceReplyCallback)> 88 OpenResourceCallback; 89 90 // Creates an adapter, using the thread associated with the given task 91 // runner for posting messages. In normal use, the task runner will post to 92 // the I/O thread of the process. 93 // 94 // If you use this constructor, you MUST call ConnectChannel after the 95 // NaClIPCAdapter is constructed, or the NaClIPCAdapter's channel will not be 96 // connected. 97 // 98 // |resolve_file_token_cb| is an optional callback to be invoked for 99 // resolving file tokens received from the renderer. When the file token 100 // is resolved, the ResolveFileTokenReplyCallback passed inside the 101 // ResolveFileTokenCallback will be invoked. |open_resource_cb| is also an 102 // optional callback to be invoked for intercepting open_resource IRT calls. 103 // |open_resource_cb| may immediately call a OpenResourceReplyCallback 104 // function to send a pre-opened resource descriptor to the untrusted side. 105 // OpenResourceCallback returns true when OpenResourceReplyCallback is called. 106 NaClIPCAdapter(const IPC::ChannelHandle& handle, 107 const scoped_refptr<base::SingleThreadTaskRunner>& runner, 108 ResolveFileTokenCallback resolve_file_token_cb, 109 OpenResourceCallback open_resource_cb); 110 111 // Initializes with a given channel that's already created for testing 112 // purposes. This function will take ownership of the given channel. 113 NaClIPCAdapter(std::unique_ptr<IPC::Channel> channel, 114 base::TaskRunner* runner); 115 116 NaClIPCAdapter(const NaClIPCAdapter&) = delete; 117 NaClIPCAdapter& operator=(const NaClIPCAdapter&) = delete; 118 119 // Connect the channel. This must be called after the constructor that accepts 120 // an IPC::ChannelHandle, and causes the Channel to be connected on the IO 121 // thread. 122 void ConnectChannel(); 123 124 // Implementation of sendmsg. Returns the number of bytes written or -1 on 125 // failure. 126 int Send(const NaClImcTypedMsgHdr* msg); 127 128 // Implementation of recvmsg. Returns the number of bytes read or -1 on 129 // failure. This will block until there's an error or there is data to 130 // read. 131 int BlockingReceive(NaClImcTypedMsgHdr* msg); 132 133 // Closes the IPC channel. 134 void CloseChannel(); 135 136 // Make a NaClDesc that refers to this NaClIPCAdapter. Note that the returned 137 // NaClDesc is reference-counted, and a reference is returned. 138 NaClDesc* MakeNaClDesc(); 139 140 // Listener implementation. 141 bool OnMessageReceived(const IPC::Message& message) override; 142 void OnChannelConnected(int32_t peer_pid) override; 143 void OnChannelError() override; 144 145 private: 146 friend class base::RefCountedThreadSafe<NaClIPCAdapter>; 147 148 class RewrittenMessage; 149 150 // This is the data that must only be accessed inside the lock. This struct 151 // just separates it so it's easier to see. 152 struct LockedData { 153 LockedData(); 154 ~LockedData(); 155 156 // Messages that we have read off of the Chrome IPC channel that are waiting 157 // to be received by the plugin. 158 base::queue<std::unique_ptr<RewrittenMessage>> to_be_received_; 159 160 ppapi::proxy::NaClMessageScanner nacl_msg_scanner_; 161 162 // Data that we've queued from the plugin to send, but doesn't consist of a 163 // full message yet. The calling code can break apart the message into 164 // smaller pieces, and we need to send the message to the other process in 165 // one chunk. 166 // 167 // The IPC channel always starts a new send() at the beginning of each 168 // message, so we don't need to worry about arbitrary message boundaries. 169 std::string to_be_sent_; 170 171 bool channel_closed_; 172 }; 173 174 // This is the data that must only be accessed on the I/O thread (as defined 175 // by TaskRunner). This struct just separates it so it's easier to see. 176 struct IOThreadData { 177 IOThreadData(); 178 ~IOThreadData(); 179 180 std::unique_ptr<IPC::Channel> channel_; 181 182 // When we send a synchronous message (from untrusted to trusted), we store 183 // its type here, so that later we can associate the reply with its type 184 // for scanning. 185 typedef std::map<int, uint32_t> PendingSyncMsgMap; 186 PendingSyncMsgMap pending_sync_msgs_; 187 }; 188 189 ~NaClIPCAdapter() override; 190 191 void SaveOpenResourceMessage(const IPC::Message& orig_msg, 192 IPC::PlatformFileForTransit ipc_fd, 193 base::FilePath file_path); 194 195 bool RewriteMessage(const IPC::Message& msg, uint32_t type); 196 197 // Returns 0 if nothing is waiting. 198 int LockedReceive(NaClImcTypedMsgHdr* msg); 199 200 // Sends a message that we know has been completed to the Chrome process. 201 bool SendCompleteMessage(const char* buffer, size_t buffer_len); 202 203 // Clears the LockedData.to_be_sent_ structure in a way to make sure that 204 // the memory is deleted. std::string can sometimes hold onto the buffer 205 // for future use which we don't want. 206 void ClearToBeSent(); 207 208 void ConnectChannelOnIOThread(); 209 void CloseChannelOnIOThread(); 210 void SendMessageOnIOThread(std::unique_ptr<IPC::Message> message); 211 212 // Saves the message to forward to NaCl. This method assumes that the caller 213 // holds the lock for locked_data_. 214 void SaveMessage(const IPC::Message& message, 215 std::unique_ptr<RewrittenMessage> rewritten_message); 216 217 base::Lock lock_; 218 base::ConditionVariable cond_var_; 219 220 scoped_refptr<base::TaskRunner> task_runner_; 221 222 ResolveFileTokenCallback resolve_file_token_cb_; 223 OpenResourceCallback open_resource_cb_; 224 225 // To be accessed inside of lock_ only. 226 LockedData locked_data_; 227 228 // To be accessed on the I/O thread (via task runner) only. 229 IOThreadData io_thread_data_; 230 }; 231 232 // Export TranslatePepperFileReadWriteOpenFlags for testing. 233 int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags); 234 235 #endif // COMPONENTS_NACL_LOADER_NACL_IPC_ADAPTER_H_ 236