summaryrefslogtreecommitdiffstats
path: root/ppapi/proxy/nacl_message_scanner.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ppapi/proxy/nacl_message_scanner.cc')
-rw-r--r--ppapi/proxy/nacl_message_scanner.cc269
1 files changed, 253 insertions, 16 deletions
diff --git a/ppapi/proxy/nacl_message_scanner.cc b/ppapi/proxy/nacl_message_scanner.cc
index 602b18e..3c2b45f8 100644
--- a/ppapi/proxy/nacl_message_scanner.cc
+++ b/ppapi/proxy/nacl_message_scanner.cc
@@ -19,12 +19,16 @@ namespace IPC {
class Message;
}
+using ppapi::proxy::ResourceMessageReplyParams;
+using ppapi::proxy::SerializedHandle;
+using ppapi::proxy::SerializedVar;
+
namespace {
-typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
+typedef std::vector<SerializedHandle> Handles;
struct ScanningResults {
- ScanningResults() : handle_index(0) {}
+ ScanningResults() : handle_index(0), pp_resource(0) {}
// Vector to hold handles found in the message.
Handles handles;
@@ -36,12 +40,18 @@ struct ScanningResults {
// 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;
+ // Resource id for resource messages. Save this when scanning resource replies
+ // so when we audit the nested message, we know which resource it is for.
+ PP_Resource pp_resource;
+ // Callback to receive the nested message in a resource message or reply.
+ base::Callback<void(PP_Resource, const IPC::Message&, SerializedHandle*)>
+ nested_msg_callback;
};
void WriteHandle(int handle_index,
- const ppapi::proxy::SerializedHandle& handle,
+ const SerializedHandle& handle,
IPC::Message* msg) {
- ppapi::proxy::SerializedHandle::WriteHeader(handle.header(), msg);
+ SerializedHandle::WriteHeader(handle.header(), msg);
// Now write the handle itself in POSIX style.
msg->WriteBool(true); // valid == true
@@ -52,8 +62,7 @@ void WriteHandle(int handle_index,
// handling. See ScanTuple for how these get used.
// Overload to match SerializedHandle.
-void ScanParam(const ppapi::proxy::SerializedHandle& handle,
- ScanningResults* results) {
+void ScanParam(const SerializedHandle& handle, ScanningResults* results) {
results->handles.push_back(handle);
if (results->new_msg)
WriteHandle(results->handle_index++, handle, results->new_msg.get());
@@ -61,14 +70,13 @@ void ScanParam(const ppapi::proxy::SerializedHandle& handle,
void HandleWriter(int* handle_index,
IPC::Message* m,
- const ppapi::proxy::SerializedHandle& handle) {
+ const 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();
+void ScanParam(const SerializedVar& var, ScanningResults* results) {
+ std::vector<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]);
@@ -82,8 +90,9 @@ void ScanParam(const ppapi::proxy::SerializedVar& var,
// 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,
+void ScanParam(const ResourceMessageReplyParams& params,
ScanningResults* results) {
+ results->pp_resource = params.pp_resource();
// 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
@@ -112,8 +121,21 @@ void ScanParam(const ppapi::proxy::ResourceMessageReplyParams& params,
params.ConsumeHandles();
}
-// Overload to match all other types. If we need to rewrite the message,
-// write the parameter.
+// Overload to match nested messages. If we need to rewrite the message, write
+// the parameter.
+void ScanParam(const IPC::Message& param, ScanningResults* results) {
+ if (results->pp_resource && !results->nested_msg_callback.is_null()) {
+ SerializedHandle* handle = NULL;
+ if (results->handles.size() == 1)
+ handle = &results->handles[0];
+ results->nested_msg_callback.Run(results->pp_resource, param, handle);
+ }
+ if (results->new_msg)
+ IPC::WriteParam(results->new_msg.get(), param);
+}
+
+// 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)
@@ -211,9 +233,58 @@ namespace proxy {
class SerializedHandle;
+NaClMessageScanner::FileSystem::FileSystem()
+ : reserved_quota_(0) {
+}
+
+NaClMessageScanner::FileSystem::~FileSystem() {
+}
+
+bool NaClMessageScanner::FileSystem::UpdateReservedQuota(int64_t delta) {
+ base::AutoLock lock(lock_);
+ if (std::numeric_limits<int64_t>::max() - reserved_quota_ < delta)
+ return false; // reserved_quota_ + delta would overflow.
+ if (reserved_quota_ + delta < 0)
+ return false;
+ reserved_quota_ += delta;
+ return true;
+}
+
+NaClMessageScanner::FileIO::FileIO(FileSystem* file_system,
+ int64_t max_written_offset)
+ : file_system_(file_system),
+ max_written_offset_(max_written_offset) {
+}
+
+NaClMessageScanner::FileIO::~FileIO() {
+}
+
+void NaClMessageScanner::FileIO::SetMaxWrittenOffset(
+ int64_t max_written_offset) {
+ base::AutoLock lock(lock_);
+ max_written_offset_ = max_written_offset;
+}
+
+bool NaClMessageScanner::FileIO::Grow(int64_t amount) {
+ base::AutoLock lock(lock_);
+ DCHECK(amount > 0);
+ if (!file_system_->UpdateReservedQuota(-amount))
+ return false;
+ max_written_offset_ += amount;
+ return true;
+}
+
NaClMessageScanner::NaClMessageScanner() {
}
+NaClMessageScanner::~NaClMessageScanner() {
+ for (FileSystemMap::iterator it = file_systems_.begin();
+ it != file_systems_.end(); ++it)
+ delete it->second;
+ for (FileIOMap::iterator it = files_.begin(); it != files_.end(); ++it)
+ delete it->second;
+}
+
// 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
@@ -239,12 +310,14 @@ bool NaClMessageScanner::ScanMessage(
(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;
+ results.nested_msg_callback =
+ base::Bind(&NaClMessageScanner::AuditNestedMessage,
+ base::Unretained(this));
switch (msg.type()) {
CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel)
CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated)
@@ -290,8 +363,113 @@ void NaClMessageScanner::ScanUntrustedMessage(
scoped_ptr<IPC::Message>* new_msg_ptr) {
if (untrusted_msg.is_sync())
RegisterSyncMessageForReply(untrusted_msg);
- // TODO(bbudge) Add message auditing for FileSystem and FileIO resources when
- // we implement Write on the plugin side of the proxy. See crbug.com/194304.
+
+ // Audit FileIO and FileSystem messages to ensure that the plugin doesn't
+ // exceed its file quota. If we find the message is malformed, just pass it
+ // through - we only care about well formed messages to the host.
+ if (untrusted_msg.type() == PpapiHostMsg_ResourceCall::ID) {
+ ResourceMessageCallParams params;
+ IPC::Message nested_msg;
+ if (!UnpackMessage<PpapiHostMsg_ResourceCall>(
+ untrusted_msg, &params, &nested_msg))
+ return;
+
+ switch (nested_msg.type()) {
+ case PpapiHostMsg_FileIO_Close::ID: {
+ FileIOMap::iterator it = files_.find(params.pp_resource());
+ if (it == files_.end())
+ return;
+ // Audit FileIO Close messages to make sure the plugin reports an
+ // accurate file size.
+ int64_t max_written_offset = 0;
+ if (!UnpackMessage<PpapiHostMsg_FileIO_Close>(
+ nested_msg, &max_written_offset))
+ return;
+
+ int64_t trusted_max_written_offset = it->second->max_written_offset();
+ delete it->second;
+ files_.erase(it);
+ // If the plugin is under-reporting, rewrite the message with the
+ // trusted value.
+ if (trusted_max_written_offset > max_written_offset) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileIO_Close(trusted_max_written_offset)));
+ }
+ }
+ case PpapiHostMsg_FileIO_SetLength::ID: {
+ FileIOMap::iterator it = files_.find(params.pp_resource());
+ if (it == files_.end())
+ return;
+ // Audit FileIO SetLength messages to make sure the plugin is within
+ // the current quota reservation. In addition, deduct the file size
+ // increase from the quota reservation.
+ int64_t length = 0;
+ if (!UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
+ nested_msg, &length))
+ return;
+
+ // Calculate file size increase, taking care to avoid overflows.
+ if (length < 0)
+ return;
+ int64_t trusted_max_written_offset = it->second->max_written_offset();
+ int64_t increase = length - trusted_max_written_offset;
+ if (increase <= 0)
+ return;
+ if (!it->second->Grow(increase)) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileIO_SetLength(-1)));
+ }
+ break;
+ }
+ case PpapiHostMsg_FileSystem_ReserveQuota::ID: {
+ // Audit FileSystem ReserveQuota messages to make sure the plugin
+ // reports accurate file sizes.
+ int64_t amount = 0;
+ FileOffsetMap max_written_offsets;
+ if (!UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
+ nested_msg, &amount, &max_written_offsets))
+ return;
+
+ bool audit_failed = false;
+ for (FileOffsetMap::iterator it = max_written_offsets.begin();
+ it != max_written_offsets.end(); ++it) {
+ FileIOMap::iterator file_it = files_.find(it->first);
+ if (file_it == files_.end())
+ continue;
+ int64_t trusted_max_written_offset =
+ file_it->second->max_written_offset();
+ if (trusted_max_written_offset > it->second) {
+ audit_failed = true;
+ it->second = trusted_max_written_offset;
+ }
+ }
+ if (audit_failed) {
+ new_msg_ptr->reset(
+ new PpapiHostMsg_ResourceCall(
+ params,
+ PpapiHostMsg_FileSystem_ReserveQuota(
+ amount, max_written_offsets)));
+ }
+ break;
+ }
+ case PpapiHostMsg_ResourceDestroyed::ID: {
+ // Audit resource destroyed messages to release FileSystems.
+ PP_Resource resource;
+ if (!UnpackMessage<PpapiHostMsg_ResourceDestroyed>(
+ nested_msg, &resource))
+ return;
+ FileSystemMap::iterator fs_it = file_systems_.find(resource);
+ if (fs_it != file_systems_.end()) {
+ delete fs_it->second;
+ file_systems_.erase(fs_it);
+ }
+ }
+ }
+ }
}
void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
@@ -301,5 +479,64 @@ void NaClMessageScanner::RegisterSyncMessageForReply(const IPC::Message& msg) {
pending_sync_msgs_[msg_id] = msg.type();
}
+NaClMessageScanner::FileIO* NaClMessageScanner::GetFile(
+ PP_Resource file_io) {
+ FileIOMap::iterator it = files_.find(file_io);
+ DCHECK(it != files_.end());
+ return it->second;
+}
+
+void NaClMessageScanner::AuditNestedMessage(PP_Resource resource,
+ const IPC::Message& msg,
+ SerializedHandle* handle) {
+ switch (msg.type()) {
+ case PpapiPluginMsg_FileIO_OpenReply::ID: {
+ // A file that requires quota checking was opened.
+ PP_Resource quota_file_system;
+ int64_t max_written_offset = 0;
+ if (ppapi::UnpackMessage<PpapiPluginMsg_FileIO_OpenReply>(
+ msg, &quota_file_system, &max_written_offset)) {
+ if (quota_file_system) {
+ // Look up the FileSystem by inserting a new one. If it was already
+ // present, get the existing one, otherwise construct it.
+ FileSystem* file_system = NULL;
+ std::pair<FileSystemMap::iterator, bool> insert_result =
+ file_systems_.insert(std::make_pair(quota_file_system,
+ file_system));
+ if (insert_result.second)
+ insert_result.first->second = new FileSystem();
+ file_system = insert_result.first->second;
+ // Create the FileIO.
+ DCHECK(files_.find(resource) == files_.end());
+ files_.insert(std::make_pair(
+ resource,
+ new FileIO(file_system, max_written_offset)));
+ }
+ }
+ break;
+ }
+ case PpapiPluginMsg_FileSystem_ReserveQuotaReply::ID: {
+ // The amount of reserved quota for a FileSystem was refreshed.
+ int64_t amount = 0;
+ FileOffsetMap max_written_offsets;
+ if (ppapi::UnpackMessage<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(
+ msg, &amount, &max_written_offsets)) {
+ FileSystemMap::iterator it = file_systems_.find(resource);
+ DCHECK(it != file_systems_.end());
+ it->second->UpdateReservedQuota(amount);
+
+ FileOffsetMap::const_iterator offset_it = max_written_offsets.begin();
+ for (; offset_it != max_written_offsets.end(); ++offset_it) {
+ FileIOMap::iterator fio_it = files_.find(offset_it->first);
+ DCHECK(fio_it != files_.end());
+ if (fio_it != files_.end())
+ fio_it->second->SetMaxWrittenOffset(offset_it->second);
+ }
+ }
+ break;
+ }
+ }
+}
+
} // namespace proxy
} // namespace ppapi