nacl_message_scanner.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/nacl_message_scanner.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ipc/ipc_message.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ipc/ipc_message_macros.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/ppapi_messages.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/resource_message_params.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/serialized_handle.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ppapi/proxy/serialized_var.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NaClDescImcShm;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace IPC {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Message;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ppapi::proxy::ResourceMessageReplyParams;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ppapi::proxy::SerializedHandle;
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)using ppapi::proxy::SerializedVar;
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)typedef std::vector<SerializedHandle> Handles;
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)struct ScanningResults {
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScanningResults() : handle_index(0), pp_resource(0) {}
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Vector to hold handles found in the message.
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Handles handles;
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Current handle index in the rewritten message. During the scan, it will be
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // be less than or equal to handles.size(). After the scan it should be equal.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int handle_index;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The rewritten message. This may be NULL, so all ScanParam overloads should
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // check for NULL before writing to it. In some cases, a ScanParam overload
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // may set this to NULL when it can determine that there are no parameters
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // that need conversion. (See the ResourceMessageReplyParams overload.)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<IPC::Message> new_msg;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Resource id for resource messages. Save this when scanning resource replies
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so when we audit the nested message, we know which resource it is for.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PP_Resource pp_resource;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Callback to receive the nested message in a resource message or reply.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)>
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      nested_msg_callback;
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void WriteHandle(int handle_index,
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 const SerializedHandle& handle,
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 IPC::Message* msg) {
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SerializedHandle::WriteHeader(handle.header(), msg);
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Now write the handle itself in POSIX style.
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  msg->WriteBool(true);  // valid == true
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  msg->WriteInt(handle_index);
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Define overloads for each kind of message parameter that requires special
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// handling. See ScanTuple for how these get used.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Overload to match SerializedHandle.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  results->handles.push_back(handle);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (results->new_msg)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WriteHandle(results->handle_index++, handle, results->new_msg.get());
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void HandleWriter(int* handle_index,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  IPC::Message* m,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const SerializedHandle& handle) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  WriteHandle((*handle_index)++, handle, m);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Overload to match SerializedVar, which can contain handles.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ScanParam(const SerializedVar& var, ScanningResults* results) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SerializedHandle*> var_handles = var.GetHandles();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Copy any handles and then rewrite the message.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < var_handles.size(); ++i)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results->handles.push_back(*var_handles[i]);
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (results->new_msg)
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    var.WriteDataToMessage(results->new_msg.get(),
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                           base::Bind(&HandleWriter, &results->handle_index));
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall,
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the handles are carried inside the ResourceMessageReplyParams.
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// NOTE: We only intercept handles from host->NaCl. The only kind of
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//       ResourceMessageParams that travels this direction is
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//       ResourceMessageReplyParams, so that's the only one we need to handle.
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ScanParam(const ResourceMessageReplyParams& params,
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               ScanningResults* results) {
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  results->pp_resource = params.pp_resource();
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the resource reply params don't contain handles, NULL the new message
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pointer to cancel further rewriting.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: This works because only handles currently need rewriting, and we
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //       know at this point that this message has none.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.handles().empty()) {
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    results->new_msg.reset(NULL);
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we need to rewrite the message, write everything before the handles
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (there's nothing after the handles).
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (results->new_msg) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    params.WriteReplyHeader(results->new_msg.get());
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // IPC writes the vector length as an int before the contents of the
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // vector.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results->new_msg->WriteInt(static_cast<int>(params.handles().size()));
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (Handles::const_iterator iter = params.handles().begin();
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       iter != params.handles().end();
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       ++iter) {
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // ScanParam will write each handle to the new message, if necessary.
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ScanParam(*iter, results);
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Tell ResourceMessageReplyParams that we have taken the handles, so it
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // shouldn't close them. The NaCl runtime will take ownership of them.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.ConsumeHandles();
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Overload to match nested messages. If we need to rewrite the message, write
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// the parameter.
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScanParam(const IPC::Message& param, ScanningResults* results) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (results->pp_resource && !results->nested_msg_callback.is_null()) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SerializedHandle* handle = NULL;
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (results->handles.size() == 1)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      handle = &results->handles[0];
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results->nested_msg_callback.Run(results->pp_resource, param, handle);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (results->new_msg)
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    IPC::WriteParam(results->new_msg.get(), param);
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Overload to match all other types. If we need to rewrite the message, write
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the parameter.
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template <class T>
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ScanParam(const T& param, ScanningResults* results) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (results->new_msg)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPC::WriteParam(results->new_msg.get(), param);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// These just break apart the given tuple and run ScanParam over each param.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The idea is to scan elements in the tuple which require special handling,
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// and write them into the |results| struct.
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template <class A>
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ScanTuple(const Tuple1<A>& t1, ScanningResults* results) {
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScanParam(t1.a, results);
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template <class A, class B>
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ScanTuple(const Tuple2<A, B>& t1, ScanningResults* results) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.a, results);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.b, results);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template <class A, class B, class C>
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScanTuple(const Tuple3<A, B, C>& t1, ScanningResults* results) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.a, results);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.b, results);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.c, results);
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template <class A, class B, class C, class D>
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ScanTuple(const Tuple4<A, B, C, D>& t1, ScanningResults* results) {
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScanParam(t1.a, results);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.b, results);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.c, results);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScanParam(t1.d, results);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)template <class MessageType>
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class MessageScannerImpl {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit MessageScannerImpl(const IPC::Message* msg)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : msg_(static_cast<const MessageType*>(msg)) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ScanMessage(ScanningResults* results) {
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    typename TupleTypes<typename MessageType::Schema::Param>::ValueTuple params;
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!MessageType::Read(msg_, &params))
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScanTuple(params, results);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool ScanReply(ScanningResults* results) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    typename TupleTypes<typename MessageType::Schema::ReplyParam>::ValueTuple
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        params;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!MessageType::ReadReplyParam(msg_, &params))
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we need to rewrite the message, write the message id first.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (results->new_msg) {
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      results->new_msg->set_reply();
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int id = IPC::SyncMessage::GetMessageId(*msg_);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      results->new_msg->WriteInt(id);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScanTuple(params, results);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(dmichael): Add ScanSyncMessage for outgoing sync messages, if we ever
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //                 need to scan those.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const MessageType* msg_;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define CASE_FOR_MESSAGE(MESSAGE_TYPE) \
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MESSAGE_TYPE::ID: { \
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (rewrite_msg) \
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          results.new_msg.reset( \
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              new IPC::Message(msg.routing_id(), msg.type(), \
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               IPC::Message::PRIORITY_NORMAL)); \
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (!scanner.ScanMessage(&results)) \
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false; \
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break; \
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define CASE_FOR_REPLY(MESSAGE_TYPE) \
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case MESSAGE_TYPE::ID: { \
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        MessageScannerImpl<MESSAGE_TYPE> scanner(&msg); \
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (rewrite_msg) \
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          results.new_msg.reset( \
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              new IPC::Message(msg.routing_id(), msg.type(), \
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               IPC::Message::PRIORITY_NORMAL)); \
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!scanner.ScanReply(&results)) \
227          return false; \
228        break; \
229      }
230
231namespace ppapi {
232namespace proxy {
233
234class SerializedHandle;
235
236NaClMessageScanner::FileSystem::FileSystem()
237    : reserved_quota_(0) {
238}
239
240NaClMessageScanner::FileSystem::~FileSystem() {
241}
242
243bool 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
253NaClMessageScanner::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
259NaClMessageScanner::FileIO::~FileIO() {
260}
261
262void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
263    int64_t max_written_offset) {
264  base::AutoLock lock(lock_);
265  max_written_offset_ = max_written_offset;
266}
267
268bool 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
277NaClMessageScanner::NaClMessageScanner() {
278}
279
280NaClMessageScanner::~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.
292bool NaClMessageScanner::ScanMessage(
293    const IPC::Message& msg,
294    uint32_t type,
295    std::vector<SerializedHandle>* handles,
296    scoped_ptr<IPC::Message>* new_msg_ptr) {
297  DCHECK(handles);
298  DCHECK(handles->empty());
299  DCHECK(new_msg_ptr);
300  DCHECK(!new_msg_ptr->get());
301
302  bool rewrite_msg =
303#if defined(OS_WIN)
304      true;
305#else
306      false;
307#endif
308
309  // We can't always tell from the message ID if rewriting is needed. Therefore,
310  // scan any message types that might contain a handle. If we later determine
311  // that there are no handles, we can cancel the rewriting by clearing the
312  // results.new_msg pointer.
313  ScanningResults results;
314  results.nested_msg_callback =
315      base::Bind(&NaClMessageScanner::AuditNestedMessage,
316                 base::Unretained(this));
317  switch (type) {
318    CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
319    CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage)
320    CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply)
321    CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer)
322    CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateSimple)
323    CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall)
324    CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory)
325    default:
326      // Do nothing for messages we don't know.
327      break;
328  }
329
330  // Only messages containing handles need to be rewritten. If no handles are
331  // found, don't return the rewritten message either. This must be changed if
332  // we ever add new param types that also require rewriting.
333  if (!results.handles.empty()) {
334    handles->swap(results.handles);
335    *new_msg_ptr = results.new_msg.Pass();
336  }
337  return true;
338}
339
340void NaClMessageScanner::ScanUntrustedMessage(
341    const IPC::Message& untrusted_msg,
342    scoped_ptr<IPC::Message>* new_msg_ptr) {
343  // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
344  // exceed its file quota. If we find the message is malformed, just pass it
345  // through - we only care about well formed messages to the host.
346  if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
347    ResourceMessageCallParams params;
348    IPC::Message nested_msg;
349    if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
350            untrusted_msg, &params, &nested_msg))
351      return;
352
353    switch (nested_msg.type()) {
354      case PpapiHostMsg_FileIO_Close::ID: {
355        FileIOMap::iterator it = files_.find(params.pp_resource());
356        if (it == files_.end())
357          return;
358        // Audit FileIO Close messages to make sure the plugin reports an
359        // accurate file size.
360        FileGrowth file_growth;
361        if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
362                nested_msg, &file_growth))
363          return;
364
365        int64_t trusted_max_written_offset = it->second->max_written_offset();
366        delete it->second;
367        files_.erase(it);
368        // If the plugin is under-reporting, rewrite the message with the
369        // trusted value.
370        if (trusted_max_written_offset > file_growth.max_written_offset) {
371          new_msg_ptr->reset(
372              new PpapiHostMsg_ResourceCall(
373                  params,
374                  PpapiHostMsg_FileIO_Close(
375                      FileGrowth(trusted_max_written_offset, 0))));
376        }
377        break;
378      }
379      case PpapiHostMsg_FileIO_SetLength::ID: {
380        FileIOMap::iterator it = files_.find(params.pp_resource());
381        if (it == files_.end())
382          return;
383        // Audit FileIO SetLength messages to make sure the plugin is within
384        // the current quota reservation. In addition, deduct the file size
385        // increase from the quota reservation.
386        int64_t length = 0;
387        if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
388                nested_msg, &length))
389          return;
390
391        // Calculate file size increase, taking care to avoid overflows.
392        if (length < 0)
393          return;
394        int64_t trusted_max_written_offset = it->second->max_written_offset();
395        int64_t increase = length - trusted_max_written_offset;
396        if (increase <= 0)
397          return;
398        if (!it->second->Grow(increase)) {
399          new_msg_ptr->reset(
400              new PpapiHostMsg_ResourceCall(
401                  params,
402                  PpapiHostMsg_FileIO_SetLength(-1)));
403        }
404        break;
405      }
406      case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
407        // Audit FileSystem ReserveQuota messages to make sure the plugin
408        // reports accurate file sizes.
409        int64_t amount = 0;
410        FileGrowthMap file_growths;
411        if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
412                nested_msg, &amount, &file_growths))
413          return;
414
415        bool audit_failed = false;
416        for (FileGrowthMap::iterator it = file_growths.begin();
417            it != file_growths.end(); ++it) {
418          FileIOMap::iterator file_it = files_.find(it->first);
419          if (file_it == files_.end())
420            continue;
421          int64_t trusted_max_written_offset =
422              file_it->second->max_written_offset();
423          if (trusted_max_written_offset > it->second.max_written_offset) {
424            audit_failed = true;
425            it->second.max_written_offset = trusted_max_written_offset;
426          }
427          if (it->second.append_mode_write_amount < 0) {
428            audit_failed = true;
429            it->second.append_mode_write_amount = 0;
430          }
431        }
432        if (audit_failed) {
433          new_msg_ptr->reset(
434              new PpapiHostMsg_ResourceCall(
435                  params,
436                  PpapiHostMsg_FileSystem_ReserveQuota(
437                      amount, file_growths)));
438        }
439        break;
440      }
441      case PpapiHostMsg_ResourceDestroyed::ID: {
442        // Audit resource destroyed messages to release FileSystems.
443        PP_Resource resource;
444        if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
445                nested_msg, &resource))
446          return;
447        FileSystemMap::iterator fs_it = file_systems_.find(resource);
448        if (fs_it != file_systems_.end()) {
449          delete fs_it->second;
450          file_systems_.erase(fs_it);
451        }
452        break;
453      }
454    }
455  }
456}
457
458NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
459    PP_Resource file_io) {
460  FileIOMap::iterator it = files_.find(file_io);
461  DCHECK(it != files_.end());
462  return it->second;
463}
464
465void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
466                                            const IPC::Message& msg,
467                                            SerializedHandle* handle) {
468  switch (msg.type()) {
469    case PpapiPluginMsg_FileIO_OpenReply::ID: {
470      // A file that requires quota checking was opened.
471      PP_Resource quota_file_system;
472      int64_t max_written_offset = 0;
473      if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
474              msg, &quota_file_system, &max_written_offset)) {
475        if (quota_file_system) {
476          // Look up the FileSystem by inserting a new one. If it was already
477          // present, get the existing one, otherwise construct it.
478          FileSystem* file_system = NULL;
479          std::pair<FileSystemMap::iterator, bool> insert_result =
480              file_systems_.insert(std::make_pair(quota_file_system,
481                                                  file_system));
482          if (insert_result.second)
483            insert_result.first->second = new FileSystem();
484          file_system = insert_result.first->second;
485          // Create the FileIO.
486          DCHECK(files_.find(resource) == files_.end());
487          files_.insert(std::make_pair(
488              resource,
489              new FileIO(file_system, max_written_offset)));
490        }
491      }
492      break;
493    }
494    case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
495      // The amount of reserved quota for a FileSystem was refreshed.
496      int64_t amount = 0;
497      FileSizeMap file_sizes;
498      if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
499          msg, &amount, &file_sizes)) {
500        FileSystemMap::iterator it = file_systems_.find(resource);
501        DCHECK(it != file_systems_.end());
502        it->second->UpdateReservedQuota(amount);
503
504        FileSizeMap::const_iterator offset_it = file_sizes.begin();
505        for (; offset_it != file_sizes.end(); ++offset_it) {
506          FileIOMap::iterator fio_it = files_.find(offset_it->first);
507          DCHECK(fio_it != files_.end());
508          if (fio_it != files_.end())
509            fio_it->second->SetMaxWrittenOffset(offset_it->second);
510        }
511      }
512      break;
513    }
514  }
515}
516
517}  // namespace proxy
518}  // namespace ppapi
519