Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / nacl_message_scanner.cc
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 using ppapi::proxy::ResourceMessageReplyParams;
23 using ppapi::proxy::SerializedHandle;
24 using ppapi::proxy::SerializedVar;
25
26 namespace {
27
28 typedef std::vector<SerializedHandle> Handles;
29
30 struct ScanningResults {
31   ScanningResults() : handle_index(0), pp_resource(0) {}
32
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;
49 };
50
51 void WriteHandle(int handle_index,
52                  const SerializedHandle& handle,
53                  IPC::Message* msg) {
54   SerializedHandle::WriteHeader(handle.header(), msg);
55
56   // Now write the handle itself in POSIX style.
57   msg->WriteBool(true);  // valid == true
58   msg->WriteInt(handle_index);
59 }
60
61 // Define overloads for each kind of message parameter that requires special
62 // handling. See ScanTuple for how these get used.
63
64 // Overload to match SerializedHandle.
65 void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
66   results->handles.push_back(handle);
67   if (results->new_msg)
68     WriteHandle(results->handle_index++, handle, results->new_msg.get());
69 }
70
71 void HandleWriter(int* handle_index,
72                   IPC::Message* m,
73                   const SerializedHandle& handle) {
74   WriteHandle((*handle_index)++, handle, m);
75 }
76
77 // Overload to match SerializedVar, which can contain handles.
78 void ScanParam(const SerializedVar& var, ScanningResults* results) {
79   std::vector<SerializedHandle*> var_handles = var.GetHandles();
80   // Copy any handles and then rewrite the message.
81   for (size_t i = 0; i < var_handles.size(); ++i)
82     results->handles.push_back(*var_handles[i]);
83   if (results->new_msg)
84     var.WriteDataToMessage(results->new_msg.get(),
85                            base::Bind(&HandleWriter, &results->handle_index));
86 }
87
88 // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
89 // the handles are carried inside the ResourceMessageReplyParams.
90 // NOTE: We only intercept handles from host->NaCl. The only kind of
91 //       ResourceMessageParams that travels this direction is
92 //       ResourceMessageReplyParams, so that's the only one we need to handle.
93 void ScanParam(const ResourceMessageReplyParams& params,
94                ScanningResults* results) {
95   results->pp_resource = params.pp_resource();
96   // If the resource reply params don't contain handles, NULL the new message
97   // pointer to cancel further rewriting.
98   // NOTE: This works because only handles currently need rewriting, and we
99   //       know at this point that this message has none.
100   if (params.handles().empty()) {
101     results->new_msg.reset(NULL);
102     return;
103   }
104
105   // If we need to rewrite the message, write everything before the handles
106   // (there's nothing after the handles).
107   if (results->new_msg) {
108     params.WriteReplyHeader(results->new_msg.get());
109     // IPC writes the vector length as an int before the contents of the
110     // vector.
111     results->new_msg->WriteInt(static_cast<int>(params.handles().size()));
112   }
113   for (Handles::const_iterator iter = params.handles().begin();
114        iter != params.handles().end();
115        ++iter) {
116     // ScanParam will write each handle to the new message, if necessary.
117     ScanParam(*iter, results);
118   }
119   // Tell ResourceMessageReplyParams that we have taken the handles, so it
120   // shouldn't close them. The NaCl runtime will take ownership of them.
121   params.ConsumeHandles();
122 }
123
124 // Overload to match nested messages. If we need to rewrite the message, write
125 // the parameter.
126 void ScanParam(const IPC::Message& param, ScanningResults* results) {
127   if (results->pp_resource && !results->nested_msg_callback.is_null()) {
128     SerializedHandle* handle = NULL;
129     if (results->handles.size() == 1)
130       handle = &results->handles[0];
131     results->nested_msg_callback.Run(results->pp_resource, param, handle);
132   }
133   if (results->new_msg)
134     IPC::WriteParam(results->new_msg.get(), param);
135 }
136
137 // Overload to match all other types. If we need to rewrite the message, write
138 // the parameter.
139 template <class T>
140 void ScanParam(const T& param, ScanningResults* results) {
141   if (results->new_msg)
142     IPC::WriteParam(results->new_msg.get(), param);
143 }
144
145 // These just break apart the given tuple and run ScanParam over each param.
146 // The idea is to scan elements in the tuple which require special handling,
147 // and write them into the |results| struct.
148 template <class A>
149 void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) {
150   ScanParam(t1.a, results);
151 }
152 template <class A, class B>
153 void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) {
154   ScanParam(t1.a, results);
155   ScanParam(t1.b, results);
156 }
157 template <class A, class B, class C>
158 void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) {
159   ScanParam(t1.a, results);
160   ScanParam(t1.b, results);
161   ScanParam(t1.c, results);
162 }
163 template <class A, class B, class C, class D>
164 void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) {
165   ScanParam(t1.a, results);
166   ScanParam(t1.b, results);
167   ScanParam(t1.c, results);
168   ScanParam(t1.d, results);
169 }
170
171 template <class MessageType>
172 class MessageScannerImpl {
173  public:
174   explicit MessageScannerImpl(const IPC::Message* msg)
175       : msg_(static_cast<const MessageType*>(msg)) {
176   }
177   bool ScanMessage(ScanningResults* results) {
178     typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params;
179     if (!MessageType::Read(msg_, &params))
180       return false;
181     ScanTuple(params, results);
182     return true;
183   }
184
185   bool ScanReply(ScanningResults* results) {
186     typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple
187         params;
188     if (!MessageType::ReadReplyParam(msg_, &params))
189       return false;
190     // If we need to rewrite the message, write the message id first.
191     if (results->new_msg) {
192       results->new_msg->set_reply();
193       int id = IPC::SyncMessage::GetMessageId(*msg_);
194       results->new_msg->WriteInt(id);
195     }
196     ScanTuple(params, results);
197     return true;
198   }
199   // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever
200   //                 need to scan those.
201
202  private:
203   const MessageType* msg_;
204 };
205
206 }  // namespace
207
208 #define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
209       case MESSAGE_TYPE::ID: { \
210         MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
211         if (rewrite_msg) \
212           results.new_msg.reset( \
213               new IPC::Message(msg.routing_id(), msg.type(), \
214                                IPC::Message::PRIORITY_NORMAL)); \
215         if (!scanner.ScanMessage(&results)) \
216           return false; \
217         break; \
218       }
219 #define CASE_FOR_REPLY(MESSAGE_TYPE) \
220       case MESSAGE_TYPE::ID: { \
221         MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
222         if (rewrite_msg) \
223           results.new_msg.reset( \
224               new IPC::Message(msg.routing_id(), msg.type(), \
225                                IPC::Message::PRIORITY_NORMAL)); \
226         if (!scanner.ScanReply(&results)) \
227           return false; \
228         break; \
229       }
230
231 namespace ppapi {
232 namespace proxy {
233
234 class SerializedHandle;
235
236 NaClMessageScanner::FileSystem::FileSystem()
237     : reserved_quota_(0) {
238 }
239
240 NaClMessageScanner::FileSystem::~FileSystem() {
241 }
242
243 bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) {
244   base::AutoLock lock(lock_);
245   if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta)
246     return false;  // reserved_quota_ + delta would overflow.
247   if (reserved_quota_ + delta < 0)
248     return false;
249   reserved_quota_ += delta;
250   return true;
251 }
252
253 NaClMessageScanner::FileIO::FileIO(FileSystem* file_system,
254                                    int64_t max_written_offset)
255     : file_system_(file_system),
256       max_written_offset_(max_written_offset) {
257 }
258
259 NaClMessageScanner::FileIO::~FileIO() {
260 }
261
262 void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
263     int64_t max_written_offset) {
264   base::AutoLock lock(lock_);
265   max_written_offset_ = max_written_offset;
266 }
267
268 bool NaClMessageScanner::FileIO::Grow(int64_t amount) {
269   base::AutoLock lock(lock_);
270   DCHECK(amount > 0);
271   if (!file_system_->UpdateReservedQuota(-amount))
272     return false;
273   max_written_offset_ += amount;
274   return true;
275 }
276
277 NaClMessageScanner::NaClMessageScanner() {
278 }
279
280 NaClMessageScanner::~NaClMessageScanner() {
281   for (FileSystemMap::iterator it = file_systems_.begin();
282       it != file_systems_.end(); ++it)
283     delete it->second;
284   for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it)
285     delete it->second;
286 }
287
288 // Windows IPC differs from POSIX in that native handles are serialized in the
289 // message body, rather than passed in a separate FileDescriptorSet. Therefore,
290 // on Windows, any message containing handles must be rewritten in the POSIX
291 // format before we can send it to the NaCl plugin.
292 bool NaClMessageScanner::ScanMessage(
293     const IPC::Message& msg,
294     std::vector<SerializedHandle>* handles,
295     scoped_ptr<IPC::Message>* new_msg_ptr) {
296   DCHECK(handles);
297   DCHECK(handles->empty());
298   DCHECK(new_msg_ptr);
299   DCHECK(!new_msg_ptr->get());
300
301   bool rewrite_msg =
302 #if defined(OS_WIN)
303       true;
304 #else
305       false;
306 #endif
307
308   // We can't always tell from the message ID if rewriting is needed. Therefore,
309   // scan any message types that might contain a handle. If we later determine
310   // that there are no handles, we can cancel the rewriting by clearing the
311   // results.new_msg pointer.
312   ScanningResults results;
313   results.nested_msg_callback =
314       base::Bind(&NaClMessageScanner::AuditNestedMessage,
315                  base::Unretained(this));
316   switch (msg.type()) {
317     CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
318     CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
319     CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
320     case IPC_REPLY_ID: {
321       int id = IPC::SyncMessage::GetMessageId(msg);
322       PendingSyncMsgMap::iterator iter(pending_sync_msgs_.find(id));
323       if (iter == pending_sync_msgs_.end()) {
324         NOTREACHED();
325         return false;
326       }
327       uint32_t type = iter->second;
328       pending_sync_msgs_.erase(iter);
329       switch (type) {
330         CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer)
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;
337       }
338       break;
339     }
340     default:
341       // Do nothing for messages we don't know.
342       break;
343   }
344
345   // Only messages containing handles need to be rewritten. If no handles are
346   // found, don't return the rewritten message either. This must be changed if
347   // we ever add new param types that also require rewriting.
348   if (!results.handles.empty()) {
349     handles->swap(results.handles);
350     *new_msg_ptr = results.new_msg.Pass();
351   }
352   return true;
353 }
354
355 void NaClMessageScanner::ScanUntrustedMessage(
356     const IPC::Message& untrusted_msg,
357     scoped_ptr<IPC::Message>* new_msg_ptr) {
358   if (untrusted_msg.is_sync())
359     RegisterSyncMessageForReply(untrusted_msg);
360
361   // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
362   // exceed its file quota. If we find the message is malformed, just pass it
363   // through - we only care about well formed messages to the host.
364   if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
365     ResourceMessageCallParams params;
366     IPC::Message nested_msg;
367     if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
368             untrusted_msg, &params, &nested_msg))
369       return;
370
371     switch (nested_msg.type()) {
372       case PpapiHostMsg_FileIO_Close::ID: {
373         FileIOMap::iterator it = files_.find(params.pp_resource());
374         if (it == files_.end())
375           return;
376         // Audit FileIO Close messages to make sure the plugin reports an
377         // accurate file size.
378         FileGrowth file_growth;
379         if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
380                 nested_msg, &file_growth))
381           return;
382
383         int64_t trusted_max_written_offset = it->second->max_written_offset();
384         delete it->second;
385         files_.erase(it);
386         // If the plugin is under-reporting, rewrite the message with the
387         // trusted value.
388         if (trusted_max_written_offset > file_growth.max_written_offset) {
389           new_msg_ptr->reset(
390               new PpapiHostMsg_ResourceCall(
391                   params,
392                   PpapiHostMsg_FileIO_Close(
393                       FileGrowth(trusted_max_written_offset, 0))));
394         }
395       }
396       case PpapiHostMsg_FileIO_SetLength::ID: {
397         FileIOMap::iterator it = files_.find(params.pp_resource());
398         if (it == files_.end())
399           return;
400         // Audit FileIO SetLength messages to make sure the plugin is within
401         // the current quota reservation. In addition, deduct the file size
402         // increase from the quota reservation.
403         int64_t length = 0;
404         if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
405                 nested_msg, &length))
406           return;
407
408         // Calculate file size increase, taking care to avoid overflows.
409         if (length < 0)
410           return;
411         int64_t trusted_max_written_offset = it->second->max_written_offset();
412         int64_t increase = length - trusted_max_written_offset;
413         if (increase <= 0)
414           return;
415         if (!it->second->Grow(increase)) {
416           new_msg_ptr->reset(
417               new PpapiHostMsg_ResourceCall(
418                   params,
419                   PpapiHostMsg_FileIO_SetLength(-1)));
420         }
421         break;
422       }
423       case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
424         // Audit FileSystem ReserveQuota messages to make sure the plugin
425         // reports accurate file sizes.
426         int64_t amount = 0;
427         FileGrowthMap file_growths;
428         if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
429                 nested_msg, &amount, &file_growths))
430           return;
431
432         bool audit_failed = false;
433         for (FileGrowthMap::iterator it = file_growths.begin();
434             it != file_growths.end(); ++it) {
435           FileIOMap::iterator file_it = files_.find(it->first);
436           if (file_it == files_.end())
437             continue;
438           int64_t trusted_max_written_offset =
439               file_it->second->max_written_offset();
440           if (trusted_max_written_offset > it->second.max_written_offset) {
441             audit_failed = true;
442             it->second.max_written_offset = trusted_max_written_offset;
443           }
444           if (it->second.append_mode_write_amount < 0) {
445             audit_failed = true;
446             it->second.append_mode_write_amount = 0;
447           }
448         }
449         if (audit_failed) {
450           new_msg_ptr->reset(
451               new PpapiHostMsg_ResourceCall(
452                   params,
453                   PpapiHostMsg_FileSystem_ReserveQuota(
454                       amount, file_growths)));
455         }
456         break;
457       }
458       case PpapiHostMsg_ResourceDestroyed::ID: {
459         // Audit resource destroyed messages to release FileSystems.
460         PP_Resource resource;
461         if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
462                 nested_msg, &resource))
463           return;
464         FileSystemMap::iterator fs_it = file_systems_.find(resource);
465         if (fs_it != file_systems_.end()) {
466           delete fs_it->second;
467           file_systems_.erase(fs_it);
468         }
469       }
470     }
471   }
472 }
473
474 void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
475   int msg_id = IPC::SyncMessage::GetMessageId(msg);
476   DCHECK(pending_sync_msgs_.find(msg_id) == pending_sync_msgs_.end());
477
478   pending_sync_msgs_[msg_id] = msg.type();
479 }
480
481 NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
482     PP_Resource file_io) {
483   FileIOMap::iterator it = files_.find(file_io);
484   DCHECK(it != files_.end());
485   return it->second;
486 }
487
488 void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
489                                             const IPC::Message& msg,
490                                             SerializedHandle* handle) {
491   switch (msg.type()) {
492     case PpapiPluginMsg_FileIO_OpenReply::ID: {
493       // A file that requires quota checking was opened.
494       PP_Resource quota_file_system;
495       int64_t max_written_offset = 0;
496       if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
497               msg, &quota_file_system, &max_written_offset)) {
498         if (quota_file_system) {
499           // Look up the FileSystem by inserting a new one. If it was already
500           // present, get the existing one, otherwise construct it.
501           FileSystem* file_system = NULL;
502           std::pair<FileSystemMap::iterator, bool> insert_result =
503               file_systems_.insert(std::make_pair(quota_file_system,
504                                                   file_system));
505           if (insert_result.second)
506             insert_result.first->second = new FileSystem();
507           file_system = insert_result.first->second;
508           // Create the FileIO.
509           DCHECK(files_.find(resource) == files_.end());
510           files_.insert(std::make_pair(
511               resource,
512               new FileIO(file_system, max_written_offset)));
513         }
514       }
515       break;
516     }
517     case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
518       // The amount of reserved quota for a FileSystem was refreshed.
519       int64_t amount = 0;
520       FileSizeMap file_sizes;
521       if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
522           msg, &amount, &file_sizes)) {
523         FileSystemMap::iterator it = file_systems_.find(resource);
524         DCHECK(it != file_systems_.end());
525         it->second->UpdateReservedQuota(amount);
526
527         FileSizeMap::const_iterator offset_it = file_sizes.begin();
528         for (; offset_it != file_sizes.end(); ++offset_it) {
529           FileIOMap::iterator fio_it = files_.find(offset_it->first);
530           DCHECK(fio_it != files_.end());
531           if (fio_it != files_.end())
532             fio_it->second->SetMaxWrittenOffset(offset_it->second);
533         }
534       }
535       break;
536     }
537   }
538 }
539
540 }  // namespace proxy
541 }  // namespace ppapi