Roll src/third_party/WebKit 88cc93e:f6ae725 (svn 200125:200141)
[chromium-blink-merge.git] / ppapi / proxy / nacl_message_scanner.cc
blob3e522f49b00a1cef190203a2a5a235d070be3a6b
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.
5 #include "ppapi/proxy/nacl_message_scanner.h"
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"
16 class NaClDescImcShm;
18 namespace IPC {
19 class Message;
22 using ppapi::proxy::ResourceMessageReplyParams;
23 using ppapi::proxy::SerializedHandle;
24 using ppapi::proxy::SerializedVar;
26 namespace {
28 typedef std::vector<SerializedHandle> Handles;
30 struct ScanningResults {
31 ScanningResults() : handle_index(0), pp_resource(0) {}
33 // Vector to hold handles found in the message.
34 Handles handles;
35 // Current handle index in the rewritten message. During the scan, it will be
36 // be less than or equal to handles.size(). After the scan it should be equal.
37 int handle_index;
38 // The rewritten message. This may be NULL, so all ScanParam overloads should
39 // check for NULL before writing to it. In some cases, a ScanParam overload
40 // may set this to NULL when it can determine that there are no parameters
41 // that need conversion. (See the ResourceMessageReplyParams overload.)
42 scoped_ptr<IPC::Message> new_msg;
43 // Resource id for resource messages. Save this when scanning resource replies
44 // so when we audit the nested message, we know which resource it is for.
45 PP_Resource pp_resource;
46 // Callback to receive the nested message in a resource message or reply.
47 base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)>
48 nested_msg_callback;
51 void WriteHandle(int handle_index,
52 const SerializedHandle& handle,
53 IPC::Message* msg) {
54 SerializedHandle::WriteHeader(handle.header(), msg);
56 if (handle.type() != SerializedHandle::INVALID) {
57 // Now write the handle itself in POSIX style.
58 // See ParamTraits<FileDescriptor>::Read for where these values are read.
59 msg->WriteBool(true); // valid == true
60 msg->WriteInt(handle_index);
64 // Define overloads for each kind of message parameter that requires special
65 // handling. See ScanTuple for how these get used.
67 // Overload to match SerializedHandle.
68 void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
69 results->handles.push_back(handle);
70 if (results->new_msg)
71 WriteHandle(results->handle_index++, handle, results->new_msg.get());
74 void HandleWriter(int* handle_index,
75 IPC::Message* m,
76 const SerializedHandle& handle) {
77 WriteHandle((*handle_index)++, handle, m);
80 // Overload to match SerializedVar, which can contain handles.
81 void ScanParam(const SerializedVar& var, ScanningResults* results) {
82 std::vector<SerializedHandle*> var_handles = var.GetHandles();
83 // Copy any handles and then rewrite the message.
84 for (size_t i = 0; i < var_handles.size(); ++i)
85 results->handles.push_back(*var_handles[i]);
86 if (results->new_msg)
87 var.WriteDataToMessage(results->new_msg.get(),
88 base::Bind(&HandleWriter, &results->handle_index));
91 // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
92 // the handles are carried inside the ResourceMessageReplyParams.
93 // NOTE: We only intercept handles from host->NaCl. The only kind of
94 // ResourceMessageParams that travels this direction is
95 // ResourceMessageReplyParams, so that's the only one we need to handle.
96 void ScanParam(const ResourceMessageReplyParams& params,
97 ScanningResults* results) {
98 results->pp_resource = params.pp_resource();
99 // If the resource reply params don't contain handles, NULL the new message
100 // pointer to cancel further rewriting.
101 // NOTE: This works because only handles currently need rewriting, and we
102 // know at this point that this message has none.
103 if (params.handles().empty()) {
104 results->new_msg.reset(NULL);
105 return;
108 // If we need to rewrite the message, write everything before the handles
109 // (there's nothing after the handles).
110 if (results->new_msg) {
111 params.WriteReplyHeader(results->new_msg.get());
112 // IPC writes the vector length as an int before the contents of the
113 // vector.
114 results->new_msg->WriteInt(static_cast<int>(params.handles().size()));
116 for (Handles::const_iterator iter = params.handles().begin();
117 iter != params.handles().end();
118 ++iter) {
119 // ScanParam will write each handle to the new message, if necessary.
120 ScanParam(*iter, results);
122 // Tell ResourceMessageReplyParams that we have taken the handles, so it
123 // shouldn't close them. The NaCl runtime will take ownership of them.
124 params.ConsumeHandles();
127 // Overload to match nested messages. If we need to rewrite the message, write
128 // the parameter.
129 void ScanParam(const IPC::Message& param, ScanningResults* results) {
130 if (results->pp_resource && !results->nested_msg_callback.is_null()) {
131 SerializedHandle* handle = NULL;
132 if (results->handles.size() == 1)
133 handle = &results->handles[0];
134 results->nested_msg_callback.Run(results->pp_resource, param, handle);
136 if (results->new_msg)
137 IPC::WriteParam(results->new_msg.get(), param);
140 // Overload to match all other types. If we need to rewrite the message, write
141 // the parameter.
142 template <class T>
143 void ScanParam(const T& param, ScanningResults* results) {
144 if (results->new_msg)
145 IPC::WriteParam(results->new_msg.get(), param);
148 // These just break apart the given tuple and run ScanParam over each param.
149 // The idea is to scan elements in the tuple which require special handling,
150 // and write them into the |results| struct.
151 template <class A>
152 void ScanTuple(const base::Tuple<A>& t1, ScanningResults* results) {
153 ScanParam(base::get<0>(t1), results);
155 template <class A, class B>
156 void ScanTuple(const base::Tuple<A, B>& t1, ScanningResults* results) {
157 ScanParam(base::get<0>(t1), results);
158 ScanParam(base::get<1>(t1), results);
160 template <class A, class B, class C>
161 void ScanTuple(const base::Tuple<A, B, C>& t1, ScanningResults* results) {
162 ScanParam(base::get<0>(t1), results);
163 ScanParam(base::get<1>(t1), results);
164 ScanParam(base::get<2>(t1), results);
166 template <class A, class B, class C, class D>
167 void ScanTuple(const base::Tuple<A, B, C, D>& t1, ScanningResults* results) {
168 ScanParam(base::get<0>(t1), results);
169 ScanParam(base::get<1>(t1), results);
170 ScanParam(base::get<2>(t1), results);
171 ScanParam(base::get<3>(t1), results);
174 template <class MessageType>
175 class MessageScannerImpl {
176 public:
177 explicit MessageScannerImpl(const IPC::Message* msg)
178 : msg_(static_cast<const MessageType*>(msg)) {
180 bool ScanMessage(ScanningResults* results) {
181 typename base::TupleTypes<typename MessageType::Schema::Param>::ValueTuple
182 params;
183 if (!MessageType::Read(msg_, &params))
184 return false;
185 ScanTuple(params, results);
186 return true;
189 bool ScanReply(ScanningResults* results) {
190 typename base::TupleTypes<typename MessageType::Schema::ReplyParam>
191 ::ValueTuple params;
192 if (!MessageType::ReadReplyParam(msg_, &params))
193 return false;
194 // If we need to rewrite the message, write the message id first.
195 if (results->new_msg) {
196 results->new_msg->set_reply();
197 int id = IPC::SyncMessage::GetMessageId(*msg_);
198 results->new_msg->WriteInt(id);
200 ScanTuple(params, results);
201 return true;
203 // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever
204 // need to scan those.
206 private:
207 const MessageType* msg_;
210 } // namespace
212 #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
213 case MESSAGE_TYPE::ID: { \
214 MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
215 if (rewrite_msg) \
216 results.new_msg.reset( \
217 new IPC::Message(msg.routing_id(), msg.type(), \
218 IPC::Message::PRIORITY_NORMAL)); \
219 if (!scanner.ScanMessage(&results)) \
220 return false; \
221 break; \
223 #define CASE_FOR_REPLY(MESSAGE_TYPE) \
224 case MESSAGE_TYPE::ID: { \
225 MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
226 if (rewrite_msg) \
227 results.new_msg.reset( \
228 new IPC::Message(msg.routing_id(), msg.type(), \
229 IPC::Message::PRIORITY_NORMAL)); \
230 if (!scanner.ScanReply(&results)) \
231 return false; \
232 break; \
235 namespace ppapi {
236 namespace proxy {
238 class SerializedHandle;
240 NaClMessageScanner::FileSystem::FileSystem()
241 : reserved_quota_(0) {
244 NaClMessageScanner::FileSystem::~FileSystem() {
247 bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) {
248 base::AutoLock lock(lock_);
249 if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta)
250 return false; // reserved_quota_ + delta would overflow.
251 if (reserved_quota_ + delta < 0)
252 return false;
253 reserved_quota_ += delta;
254 return true;
257 NaClMessageScanner::FileIO::FileIO(FileSystem* file_system,
258 int64_t max_written_offset)
259 : file_system_(file_system),
260 max_written_offset_(max_written_offset) {
263 NaClMessageScanner::FileIO::~FileIO() {
266 void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
267 int64_t max_written_offset) {
268 base::AutoLock lock(lock_);
269 max_written_offset_ = max_written_offset;
272 bool NaClMessageScanner::FileIO::Grow(int64_t amount) {
273 base::AutoLock lock(lock_);
274 DCHECK(amount > 0);
275 if (!file_system_->UpdateReservedQuota(-amount))
276 return false;
277 max_written_offset_ += amount;
278 return true;
281 NaClMessageScanner::NaClMessageScanner() {
284 NaClMessageScanner::~NaClMessageScanner() {
285 for (FileSystemMap::iterator it = file_systems_.begin();
286 it != file_systems_.end(); ++it)
287 delete it->second;
288 for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it)
289 delete it->second;
292 // Windows IPC differs from POSIX in that native handles are serialized in the
293 // message body, rather than passed in a separate FileDescriptorSet. Therefore,
294 // on Windows, any message containing handles must be rewritten in the POSIX
295 // format before we can send it to the NaCl plugin.
296 // On Mac, base::SharedMemoryHandle has a different serialization than
297 // base::FileDescriptor (which base::SharedMemoryHandle is typedef-ed to in
298 // OS_NACL).
299 bool NaClMessageScanner::ScanMessage(
300 const IPC::Message& msg,
301 uint32_t type,
302 std::vector<SerializedHandle>* handles,
303 scoped_ptr<IPC::Message>* new_msg_ptr) {
304 DCHECK(handles);
305 DCHECK(handles->empty());
306 DCHECK(new_msg_ptr);
307 DCHECK(!new_msg_ptr->get());
309 bool rewrite_msg =
310 #if defined(OS_WIN) || defined(OS_MACOSX)
311 true;
312 #else
313 false;
314 #endif
316 // We can't always tell from the message ID if rewriting is needed. Therefore,
317 // scan any message types that might contain a handle. If we later determine
318 // that there are no handles, we can cancel the rewriting by clearing the
319 // results.new_msg pointer.
320 ScanningResults results;
321 results.nested_msg_callback =
322 base::Bind(&NaClMessageScanner::AuditNestedMessage,
323 base::Unretained(this));
324 switch (type) {
325 CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
326 CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
327 CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
328 CASE_FOR_REPLY(PpapiHostMsg_OpenResource)
329 CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_Create)
330 CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer)
331 CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple)
332 CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall)
333 CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory)
334 default:
335 // Do nothing for messages we don't know.
336 break;
339 // Only messages containing handles need to be rewritten. If no handles are
340 // found, don't return the rewritten message either. This must be changed if
341 // we ever add new param types that also require rewriting.
342 if (!results.handles.empty()) {
343 handles->swap(results.handles);
344 *new_msg_ptr = results.new_msg.Pass();
346 return true;
349 void NaClMessageScanner::ScanUntrustedMessage(
350 const IPC::Message& untrusted_msg,
351 scoped_ptr<IPC::Message>* new_msg_ptr) {
352 // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
353 // exceed its file quota. If we find the message is malformed, just pass it
354 // through - we only care about well formed messages to the host.
355 if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
356 ResourceMessageCallParams params;
357 IPC::Message nested_msg;
358 if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
359 untrusted_msg, &params, &nested_msg))
360 return;
362 switch (nested_msg.type()) {
363 case PpapiHostMsg_FileIO_Close::ID: {
364 FileIOMap::iterator it = files_.find(params.pp_resource());
365 if (it == files_.end())
366 return;
367 // Audit FileIO Close messages to make sure the plugin reports an
368 // accurate file size.
369 FileGrowth file_growth;
370 if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
371 nested_msg, &file_growth))
372 return;
374 int64_t trusted_max_written_offset = it->second->max_written_offset();
375 delete it->second;
376 files_.erase(it);
377 // If the plugin is under-reporting, rewrite the message with the
378 // trusted value.
379 if (trusted_max_written_offset > file_growth.max_written_offset) {
380 new_msg_ptr->reset(
381 new PpapiHostMsg_ResourceCall(
382 params,
383 PpapiHostMsg_FileIO_Close(
384 FileGrowth(trusted_max_written_offset, 0))));
386 break;
388 case PpapiHostMsg_FileIO_SetLength::ID: {
389 FileIOMap::iterator it = files_.find(params.pp_resource());
390 if (it == files_.end())
391 return;
392 // Audit FileIO SetLength messages to make sure the plugin is within
393 // the current quota reservation. In addition, deduct the file size
394 // increase from the quota reservation.
395 int64_t length = 0;
396 if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
397 nested_msg, &length))
398 return;
400 // Calculate file size increase, taking care to avoid overflows.
401 if (length < 0)
402 return;
403 int64_t trusted_max_written_offset = it->second->max_written_offset();
404 int64_t increase = length - trusted_max_written_offset;
405 if (increase <= 0)
406 return;
407 if (!it->second->Grow(increase)) {
408 new_msg_ptr->reset(
409 new PpapiHostMsg_ResourceCall(
410 params,
411 PpapiHostMsg_FileIO_SetLength(-1)));
413 break;
415 case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
416 // Audit FileSystem ReserveQuota messages to make sure the plugin
417 // reports accurate file sizes.
418 int64_t amount = 0;
419 FileGrowthMap file_growths;
420 if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
421 nested_msg, &amount, &file_growths))
422 return;
424 bool audit_failed = false;
425 for (FileGrowthMap::iterator it = file_growths.begin();
426 it != file_growths.end(); ++it) {
427 FileIOMap::iterator file_it = files_.find(it->first);
428 if (file_it == files_.end())
429 continue;
430 int64_t trusted_max_written_offset =
431 file_it->second->max_written_offset();
432 if (trusted_max_written_offset > it->second.max_written_offset) {
433 audit_failed = true;
434 it->second.max_written_offset = trusted_max_written_offset;
436 if (it->second.append_mode_write_amount < 0) {
437 audit_failed = true;
438 it->second.append_mode_write_amount = 0;
441 if (audit_failed) {
442 new_msg_ptr->reset(
443 new PpapiHostMsg_ResourceCall(
444 params,
445 PpapiHostMsg_FileSystem_ReserveQuota(
446 amount, file_growths)));
448 break;
450 case PpapiHostMsg_ResourceDestroyed::ID: {
451 // Audit resource destroyed messages to release FileSystems.
452 PP_Resource resource;
453 if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
454 nested_msg, &resource))
455 return;
456 FileSystemMap::iterator fs_it = file_systems_.find(resource);
457 if (fs_it != file_systems_.end()) {
458 delete fs_it->second;
459 file_systems_.erase(fs_it);
461 break;
467 NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
468 PP_Resource file_io) {
469 FileIOMap::iterator it = files_.find(file_io);
470 DCHECK(it != files_.end());
471 return it->second;
474 void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
475 const IPC::Message& msg,
476 SerializedHandle* handle) {
477 switch (msg.type()) {
478 case PpapiPluginMsg_FileIO_OpenReply::ID: {
479 // A file that requires quota checking was opened.
480 PP_Resource quota_file_system;
481 int64_t max_written_offset = 0;
482 if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
483 msg, &quota_file_system, &max_written_offset)) {
484 if (quota_file_system) {
485 // Look up the FileSystem by inserting a new one. If it was already
486 // present, get the existing one, otherwise construct it.
487 FileSystem* file_system = NULL;
488 std::pair<FileSystemMap::iterator, bool> insert_result =
489 file_systems_.insert(std::make_pair(quota_file_system,
490 file_system));
491 if (insert_result.second)
492 insert_result.first->second = new FileSystem();
493 file_system = insert_result.first->second;
494 // Create the FileIO.
495 DCHECK(files_.find(resource) == files_.end());
496 files_.insert(std::make_pair(
497 resource,
498 new FileIO(file_system, max_written_offset)));
501 break;
503 case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
504 // The amount of reserved quota for a FileSystem was refreshed.
505 int64_t amount = 0;
506 FileSizeMap file_sizes;
507 if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
508 msg, &amount, &file_sizes)) {
509 FileSystemMap::iterator it = file_systems_.find(resource);
510 DCHECK(it != file_systems_.end());
511 it->second->UpdateReservedQuota(amount);
513 FileSizeMap::const_iterator offset_it = file_sizes.begin();
514 for (; offset_it != file_sizes.end(); ++offset_it) {
515 FileIOMap::iterator fio_it = files_.find(offset_it->first);
516 DCHECK(fio_it != files_.end());
517 if (fio_it != files_.end())
518 fio_it->second->SetMaxWrittenOffset(offset_it->second);
521 break;
526 } // namespace proxy
527 } // namespace ppapi