summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorbbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-28 17:54:27 +0000
committerbbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-28 17:54:27 +0000
commit7a90b856e3b08d2386750640d938d27773c08a94 (patch)
treeaac5f475aba781ad4b5653f6543e1f0d81437dfd /components
parent012a53cdbf8550a781776acf17953e4010df3be8 (diff)
downloadchromium_src-7a90b856e3b08d2386750640d938d27773c08a94.zip
chromium_src-7a90b856e3b08d2386750640d938d27773c08a94.tar.gz
chromium_src-7a90b856e3b08d2386750640d938d27773c08a94.tar.bz2
Do PPB_FileIO Write on the plugin side.
This eliminates IPC for FileIO.Write, instead using the plugin's file descriptor. Performs the file op on the plugin's thread if blocking, otherwise on the file thread. FileIOResources know their maximum written offset after they are opened. They can thus calculate whether a Write or SetLength operation will extend this max offset. If so, they call FileSystemResource::RequestQuota. This can return synchronously or asynchronously, and either returns the requested amount or 0 in case it can't be satisfied. FileSystemResource will request a quota reservation from the host, queuing up unsatisfied requests. The quota reservation will be at least 1MB with this CL. The main point of a quota reservation is to reduce the number of times we query the quota system. Both the FileSystemResource and the host maintain a collection of open files that are subject to quota. These are kept in sync. Maps that take PP_Resource to resource or host are used to ensure good performance and make it easier to keep the resource and host collections synced. SetLength uses the plugin side machinery to request quota. When its request is granted, it calls over to the host side as before. This is because of OS X sandbox restrictions. For trusted plugins, I assume that this plugin / host checking is sufficient. In particular, SetLength is performed in the browser process without checking the length against quota restrictions. We're essentially assuming trusted plugins don't cheat. For untrusted plugins, this approach isn't sufficient. NaClMessageScanner audits the FIleSystem and FileIO message traffic to maintain a parallel accounting of quota reservation and current file sizes. In addition, we wrap the native file handle in a NaClDescQuota for files that need quota checking. This NaClDescQuota uses NaClMessageScanner information to determine whether or not to allow a Write. We must check at the descriptor level since the untrusted plugin may bypass our proxy code and use the file handle directly to Write. We must also fail any attempt to call ftruncate directly. BUG=194304 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=242659 Review URL: https://codereview.chromium.org/100703004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242691 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/nacl/loader/nacl_ipc_adapter.cc122
1 files changed, 111 insertions, 11 deletions
diff --git a/components/nacl/loader/nacl_ipc_adapter.cc b/components/nacl/loader/nacl_ipc_adapter.cc
index c6aaf61..2feabf5 100644
--- a/components/nacl/loader/nacl_ipc_adapter.cc
+++ b/components/nacl/loader/nacl_ipc_adapter.cc
@@ -19,6 +19,8 @@
#include "native_client/src/trusted/desc/nacl_desc_custom.h"
#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
#include "native_client/src/trusted/desc/nacl_desc_io.h"
+#include "native_client/src/trusted/desc/nacl_desc_quota.h"
+#include "native_client/src/trusted/desc/nacl_desc_quota_interface.h"
#include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
@@ -26,6 +28,8 @@
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/serialized_handle.h"
+using ppapi::proxy::NaClMessageScanner;
+
namespace {
enum BufferSizeStatus {
@@ -55,11 +59,12 @@ BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
return MESSAGE_IS_TRUNCATED;
}
+//------------------------------------------------------------------------------
// This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
// forward calls to it.
struct DescThunker {
- explicit DescThunker(NaClIPCAdapter* adapter_param)
- : adapter(adapter_param) {
+ explicit DescThunker(NaClIPCAdapter* adapter_arg)
+ : adapter(adapter_arg) {
}
scoped_refptr<NaClIPCAdapter> adapter;
};
@@ -92,6 +97,96 @@ NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
}
+//------------------------------------------------------------------------------
+// This object is passed to a NaClDescQuota to intercept writes and forward them
+// to the NaClIPCAdapter, which checks quota. This is a NaCl-style struct. Don't
+// add non-trivial fields or virtual methods. Construction should use malloc,
+// because this is owned by the NaClDesc, and the NaCl Dtor code will call free.
+struct QuotaInterface {
+ // The "base" struct must be first. NaCl code expects a NaCl style ref-counted
+ // object, so the "vtable" and other base class fields must be first.
+ struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS;
+
+ NaClMessageScanner::FileIO* file_io;
+};
+
+static void QuotaInterfaceDtor(NaClRefCount* nrcp) {
+ // Trivial class, just pass through to the "base" struct Dtor.
+ nrcp->vtbl = reinterpret_cast<NaClRefCountVtbl*>(
+ const_cast<NaClDescQuotaInterfaceVtbl*>(&kNaClDescQuotaInterfaceVtbl));
+ (*nrcp->vtbl->Dtor)(nrcp);
+}
+
+static int64_t QuotaInterfaceWriteRequest(NaClDescQuotaInterface* ndqi,
+ const uint8_t* /* unused_id */,
+ int64_t offset,
+ int64_t length) {
+ if (offset < 0 || length < 0)
+ return 0;
+ if (std::numeric_limits<int64_t>::max() - length < offset)
+ return 0; // offset + length would overflow.
+ int64_t max_offset = offset + length;
+ if (max_offset < 0)
+ return 0;
+
+ QuotaInterface* quota_interface = reinterpret_cast<QuotaInterface*>(ndqi);
+ NaClMessageScanner::FileIO* file_io = quota_interface->file_io;
+ int64_t increase = max_offset - file_io->max_written_offset();
+ if (increase <= 0 || file_io->Grow(increase))
+ return length;
+
+ return 0;
+}
+
+static int64_t QuotaInterfaceFtruncateRequest(NaClDescQuotaInterface* ndqi,
+ const uint8_t* /* unused_id */,
+ int64_t length) {
+ // We can't implement SetLength on the plugin side due to sandbox limitations.
+ // See crbug.com/156077.
+ NOTREACHED();
+ return 0;
+}
+
+static const struct NaClDescQuotaInterfaceVtbl kQuotaInterfaceVtbl = {
+ {
+ QuotaInterfaceDtor
+ },
+ QuotaInterfaceWriteRequest,
+ QuotaInterfaceFtruncateRequest
+};
+
+NaClDesc* MakeNaClDescQuota(
+ NaClMessageScanner::FileIO* file_io,
+ NaClDesc* wrapped_desc) {
+ // Create the QuotaInterface.
+ QuotaInterface* quota_interface =
+ static_cast<QuotaInterface*>(malloc(sizeof *quota_interface));
+ if (quota_interface && NaClDescQuotaInterfaceCtor(&quota_interface->base)) {
+ quota_interface->base.base.vtbl =
+ (struct NaClRefCountVtbl *)(&kQuotaInterfaceVtbl);
+ // QuotaInterface is a trivial class, so skip the ctor.
+ quota_interface->file_io = file_io;
+ // Create the NaClDescQuota.
+ NaClDescQuota* desc = static_cast<NaClDescQuota*>(malloc(sizeof *desc));
+ uint8_t unused_id[NACL_DESC_QUOTA_FILE_ID_LEN] = {0};
+ if (desc && NaClDescQuotaCtor(desc,
+ wrapped_desc,
+ unused_id,
+ &quota_interface->base)) {
+ return &desc->base;
+ }
+ if (desc)
+ NaClDescUnref(reinterpret_cast<NaClDesc*>(desc));
+ }
+
+ if (quota_interface)
+ NaClDescQuotaInterfaceUnref(&quota_interface->base);
+
+ return NULL;
+}
+
+//------------------------------------------------------------------------------
+
void DeleteChannel(IPC::Channel* channel) {
delete channel;
}
@@ -432,21 +527,26 @@ bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
base::Passed(&response)));
break;
}
- case ppapi::proxy::SerializedHandle::FILE:
- // IMPORTANT: The NaClDescIoDescFromHandleAllocCtor function creates
- // a NaClDesc that checks file flags before reading and writing. This
- // is essential since PPB_FileIO now sends a file descriptor to the
- // plugin which may have write capabilities. We can't allow the plugin
- // to write with it since it could bypass quota checks, which still
- // happen in the host.
- nacl_desc.reset(new NaClDescWrapper(NaClDescIoDescFromHandleAllocCtor(
+ case ppapi::proxy::SerializedHandle::FILE: {
+ // Create the NaClDesc for the file descriptor. If quota checking is
+ // required, wrap it in a NaClDescQuota.
+ NaClDesc* desc = NaClDescIoDescFromHandleAllocCtor(
#if defined(OS_WIN)
iter->descriptor(),
#else
iter->descriptor().fd,
#endif
- TranslatePepperFileReadWriteOpenFlags(iter->open_flags()))));
+ TranslatePepperFileReadWriteOpenFlags(iter->open_flags()));
+ if (desc && iter->file_io()) {
+ desc = MakeNaClDescQuota(
+ locked_data_.nacl_msg_scanner_.GetFile(iter->file_io()),
+ desc);
+ }
+ if (desc)
+ nacl_desc.reset(new NaClDescWrapper(desc));
break;
+ }
+
case ppapi::proxy::SerializedHandle::INVALID: {
// Nothing to do. TODO(dmichael): Should we log this? Or is it
// sometimes okay to pass an INVALID handle?