diff options
author | bbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-28 17:54:27 +0000 |
---|---|---|
committer | bbudge@chromium.org <bbudge@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-28 17:54:27 +0000 |
commit | 7a90b856e3b08d2386750640d938d27773c08a94 (patch) | |
tree | aac5f475aba781ad4b5653f6543e1f0d81437dfd /components | |
parent | 012a53cdbf8550a781776acf17953e4010df3be8 (diff) | |
download | chromium_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.cc | 122 |
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("a_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, + "a_interface->base)) { + return &desc->base; + } + if (desc) + NaClDescUnref(reinterpret_cast<NaClDesc*>(desc)); + } + + if (quota_interface) + NaClDescQuotaInterfaceUnref("a_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? |