This source file includes following definitions.
- GetBufferStatus
- ToAdapter
- NaClDescCustomDestroy
- NaClDescCustomSendMsg
- NaClDescCustomRecvMsg
- MakeNaClDescCustom
- QuotaInterfaceDtor
- QuotaInterfaceWriteRequest
- QuotaInterfaceFtruncateRequest
- MakeNaClDescQuota
- DeleteChannel
- TranslatePepperFileReadWriteOpenFlags
- desc
- is_consumed
- AddDescriptor
- desc_count
- data_read_cursor_
- SetData
- Read
- locked_data_
- locked_data_
- ConnectChannel
- Send
- BlockingReceive
- CloseChannel
- MakeNaClDesc
- TakeClientFileDescriptor
- OnMessageReceived
- OnChannelConnected
- OnChannelError
- LockedReceive
- SendCompleteMessage
- ClearToBeSent
- ConnectChannelOnIOThread
- CloseChannelOnIOThread
- SendMessageOnIOThread
- SaveMessage
- TranslatePepperFileReadWriteOpenFlagsForTesting
#include "components/nacl/loader/nacl_ipc_adapter.h"
#include <limits.h>
#include <string.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
#include "build/build_config.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_platform_file.h"
#include "native_client/src/trusted/desc/nacl_desc_base.h"
#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"
#include "ppapi/c/ppb_file_io.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/serialized_handle.h"
using ppapi::proxy::NaClMessageScanner;
namespace {
enum BufferSizeStatus {
MESSAGE_IS_COMPLETE,
MESSAGE_IS_TRUNCATED,
MESSAGE_HAS_EXTRA_DATA
};
BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
return MESSAGE_IS_TRUNCATED;
const NaClIPCAdapter::NaClMessageHeader* header =
reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
uint32 message_size =
sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
if (len == message_size)
return MESSAGE_IS_COMPLETE;
if (len > message_size)
return MESSAGE_HAS_EXTRA_DATA;
return MESSAGE_IS_TRUNCATED;
}
struct DescThunker {
explicit DescThunker(NaClIPCAdapter* adapter_arg)
: adapter(adapter_arg) {
}
scoped_refptr<NaClIPCAdapter> adapter;
};
NaClIPCAdapter* ToAdapter(void* handle) {
return static_cast<DescThunker*>(handle)->adapter.get();
}
void NaClDescCustomDestroy(void* handle) {
delete static_cast<DescThunker*>(handle);
}
ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
int ) {
return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
}
ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
int ) {
return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
}
NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
funcs.Destroy = NaClDescCustomDestroy;
funcs.SendMsg = NaClDescCustomSendMsg;
funcs.RecvMsg = NaClDescCustomRecvMsg;
return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
}
struct QuotaInterface {
struct NaClDescQuotaInterface base NACL_IS_REFCOUNT_SUBCLASS;
NaClMessageScanner::FileIO* file_io;
};
static void QuotaInterfaceDtor(NaClRefCount* nrcp) {
nrcp->vtbl = reinterpret_cast<NaClRefCountVtbl*>(
const_cast<NaClDescQuotaInterfaceVtbl*>(&kNaClDescQuotaInterfaceVtbl));
(*nrcp->vtbl->Dtor)(nrcp);
}
static int64_t QuotaInterfaceWriteRequest(NaClDescQuotaInterface* ndqi,
const uint8_t* ,
int64_t offset,
int64_t length) {
if (offset < 0 || length < 0)
return 0;
if (std::numeric_limits<int64_t>::max() - length < offset)
return 0;
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* ,
int64_t length) {
NOTREACHED();
return 0;
}
static const struct NaClDescQuotaInterfaceVtbl kQuotaInterfaceVtbl = {
{
QuotaInterfaceDtor
},
QuotaInterfaceWriteRequest,
QuotaInterfaceFtruncateRequest
};
NaClDesc* MakeNaClDescQuota(
NaClMessageScanner::FileIO* file_io,
NaClDesc* wrapped_desc) {
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);
quota_interface->file_io = file_io;
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;
}
int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags) {
bool read = (pp_open_flags & PP_FILEOPENFLAG_READ) != 0;
bool write = (pp_open_flags & PP_FILEOPENFLAG_WRITE) != 0;
bool append = (pp_open_flags & PP_FILEOPENFLAG_APPEND) != 0;
int nacl_open_flag = NACL_ABI_O_RDONLY;
if (read && (write || append)) {
nacl_open_flag = NACL_ABI_O_RDWR;
} else if (write || append) {
nacl_open_flag = NACL_ABI_O_WRONLY;
} else if (!read) {
DLOG(WARNING) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
<< "or PP_FILEOPENFLAG_APPEND should be set.";
}
if (append)
nacl_open_flag |= NACL_ABI_O_APPEND;
return nacl_open_flag;
}
class NaClDescWrapper {
public:
explicit NaClDescWrapper(NaClDesc* desc): desc_(desc) {}
~NaClDescWrapper() {
NaClDescUnref(desc_);
}
NaClDesc* desc() { return desc_; }
private:
NaClDesc* desc_;
DISALLOW_COPY_AND_ASSIGN(NaClDescWrapper);
};
}
class NaClIPCAdapter::RewrittenMessage
: public base::RefCounted<RewrittenMessage> {
public:
RewrittenMessage();
bool is_consumed() const { return data_read_cursor_ == data_len_; }
void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
const void* payload, size_t payload_length);
int Read(NaClImcTypedMsgHdr* msg);
void AddDescriptor(NaClDescWrapper* desc) { descs_.push_back(desc); }
size_t desc_count() const { return descs_.size(); }
private:
friend class base::RefCounted<RewrittenMessage>;
~RewrittenMessage() {}
scoped_ptr<char[]> data_;
size_t data_len_;
size_t data_read_cursor_;
ScopedVector<NaClDescWrapper> descs_;
};
NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
: data_len_(0),
data_read_cursor_(0) {
}
void NaClIPCAdapter::RewrittenMessage::SetData(
const NaClIPCAdapter::NaClMessageHeader& header,
const void* payload,
size_t payload_length) {
DCHECK(!data_.get() && data_len_ == 0);
size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
data_len_ = header_len + payload_length;
data_.reset(new char[data_len_]);
memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
memcpy(&data_[header_len], payload, payload_length);
}
int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
CHECK(data_len_ >= data_read_cursor_);
char* dest_buffer = static_cast<char*>(msg->iov[0].base);
size_t dest_buffer_size = msg->iov[0].length;
size_t bytes_to_write = std::min(dest_buffer_size,
data_len_ - data_read_cursor_);
if (bytes_to_write == 0)
return 0;
memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
data_read_cursor_ += bytes_to_write;
if (is_consumed()) {
nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
CHECK(desc_count <= msg->ndesc_length);
msg->ndesc_length = desc_count;
for (nacl_abi_size_t i = 0; i < desc_count; i++) {
msg->ndescv[i] = descs_[i]->desc();
NaClDescRef(descs_[i]->desc());
}
descs_.clear();
} else {
msg->ndesc_length = 0;
}
return static_cast<int>(bytes_to_write);
}
NaClIPCAdapter::LockedData::LockedData()
: channel_closed_(false) {
}
NaClIPCAdapter::LockedData::~LockedData() {
}
NaClIPCAdapter::IOThreadData::IOThreadData() {
}
NaClIPCAdapter::IOThreadData::~IOThreadData() {
}
NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
base::TaskRunner* runner)
: lock_(),
cond_var_(&lock_),
task_runner_(runner),
locked_data_() {
io_thread_data_.channel_.reset(
new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
}
NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
base::TaskRunner* runner)
: lock_(),
cond_var_(&lock_),
task_runner_(runner),
locked_data_() {
io_thread_data_.channel_ = channel.Pass();
}
void NaClIPCAdapter::ConnectChannel() {
task_runner_->PostTask(FROM_HERE,
base::Bind(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
}
int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
if (msg->iov_length != 1)
return -1;
base::AutoLock lock(lock_);
const char* input_data = static_cast<char*>(msg->iov[0].base);
size_t input_data_len = msg->iov[0].length;
if (input_data_len > IPC::Channel::kMaximumMessageSize) {
ClearToBeSent();
return -1;
}
const char* current_message;
size_t current_message_len;
bool did_append_input_data;
if (locked_data_.to_be_sent_.empty()) {
current_message = input_data;
current_message_len = input_data_len;
did_append_input_data = false;
} else {
COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
MaximumMessageSizeWillOverflow);
size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
if (new_size > IPC::Channel::kMaximumMessageSize) {
ClearToBeSent();
return -1;
}
locked_data_.to_be_sent_.append(input_data, input_data_len);
current_message = &locked_data_.to_be_sent_[0];
current_message_len = locked_data_.to_be_sent_.size();
did_append_input_data = true;
}
switch (GetBufferStatus(current_message, current_message_len)) {
case MESSAGE_IS_COMPLETE: {
bool success = SendCompleteMessage(current_message, current_message_len);
ClearToBeSent();
return success ? static_cast<int>(input_data_len) : -1;
}
case MESSAGE_IS_TRUNCATED:
if (!did_append_input_data)
locked_data_.to_be_sent_.append(input_data, input_data_len);
return static_cast<int>(input_data_len);
case MESSAGE_HAS_EXTRA_DATA:
default:
ClearToBeSent();
return -1;
}
}
int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
if (msg->iov_length != 1)
return -1;
int retval = 0;
{
base::AutoLock lock(lock_);
while (locked_data_.to_be_received_.empty() &&
!locked_data_.channel_closed_)
cond_var_.Wait();
if (locked_data_.channel_closed_) {
retval = -1;
} else {
retval = LockedReceive(msg);
DCHECK(retval > 0);
}
}
cond_var_.Signal();
return retval;
}
void NaClIPCAdapter::CloseChannel() {
{
base::AutoLock lock(lock_);
locked_data_.channel_closed_ = true;
}
cond_var_.Signal();
task_runner_->PostTask(FROM_HERE,
base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
}
NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
return MakeNaClDescCustom(this);
}
#if defined(OS_POSIX)
int NaClIPCAdapter::TakeClientFileDescriptor() {
return io_thread_data_.channel_->TakeClientFileDescriptor();
}
#endif
bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
{
base::AutoLock lock(lock_);
scoped_refptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
Handles handles;
scoped_ptr<IPC::Message> new_msg;
if (!locked_data_.nacl_msg_scanner_.ScanMessage(msg, &handles, &new_msg))
return false;
for (Handles::const_iterator iter = handles.begin();
iter != handles.end();
++iter) {
scoped_ptr<NaClDescWrapper> nacl_desc;
switch (iter->type()) {
case ppapi::proxy::SerializedHandle::SHARED_MEMORY: {
const base::SharedMemoryHandle& shm_handle = iter->shmem();
uint32_t size = iter->size();
nacl_desc.reset(new NaClDescWrapper(NaClDescImcShmMake(
#if defined(OS_WIN)
shm_handle,
#else
shm_handle.fd,
#endif
static_cast<size_t>(size))));
break;
}
case ppapi::proxy::SerializedHandle::SOCKET: {
nacl_desc.reset(new NaClDescWrapper(NaClDescSyncSocketMake(
#if defined(OS_WIN)
iter->descriptor()
#else
iter->descriptor().fd
#endif
)));
break;
}
case ppapi::proxy::SerializedHandle::FILE: {
NaClDesc* desc = NaClDescIoDescFromHandleAllocCtor(
#if defined(OS_WIN)
iter->descriptor(),
#else
iter->descriptor().fd,
#endif
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: {
break;
}
}
if (nacl_desc.get())
rewritten_msg->AddDescriptor(nacl_desc.release());
}
if (new_msg)
SaveMessage(*new_msg, rewritten_msg.get());
else
SaveMessage(msg, rewritten_msg.get());
}
cond_var_.Signal();
return true;
}
void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
}
void NaClIPCAdapter::OnChannelError() {
CloseChannel();
}
NaClIPCAdapter::~NaClIPCAdapter() {
task_runner_->PostTask(FROM_HERE,
base::Bind(&DeleteChannel, io_thread_data_.channel_.release()));
}
int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
lock_.AssertAcquired();
if (locked_data_.to_be_received_.empty())
return 0;
scoped_refptr<RewrittenMessage> current =
locked_data_.to_be_received_.front();
int retval = current->Read(msg);
if (current->is_consumed())
locked_data_.to_be_received_.pop();
return retval;
}
bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
size_t buffer_len) {
lock_.AssertAcquired();
const NaClMessageHeader* header =
reinterpret_cast<const NaClMessageHeader*>(buffer);
int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
DCHECK(body_len == static_cast<int>(header->payload_size));
scoped_ptr<IPC::Message> msg(
new IPC::Message(header->routing, header->type,
IPC::Message::PRIORITY_NORMAL));
if (header->flags & IPC::Message::SYNC_BIT)
msg->set_sync();
if (header->flags & IPC::Message::REPLY_BIT)
msg->set_reply();
if (header->flags & IPC::Message::REPLY_ERROR_BIT)
msg->set_reply_error();
if (header->flags & IPC::Message::UNBLOCK_BIT)
msg->set_unblock(true);
msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
lock_.AssertAcquired();
if (locked_data_.channel_closed_) {
return false;
}
scoped_ptr<IPC::Message> new_msg;
locked_data_.nacl_msg_scanner_.ScanUntrustedMessage(*msg, &new_msg);
if (new_msg)
msg.reset(new_msg.release());
task_runner_->PostTask(FROM_HERE,
base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
base::Passed(&msg)));
return true;
}
void NaClIPCAdapter::ClearToBeSent() {
lock_.AssertAcquired();
std::string empty;
locked_data_.to_be_sent_.swap(empty);
}
void NaClIPCAdapter::ConnectChannelOnIOThread() {
if (!io_thread_data_.channel_->Connect())
NOTREACHED();
}
void NaClIPCAdapter::CloseChannelOnIOThread() {
io_thread_data_.channel_->Close();
}
void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
io_thread_data_.channel_->Send(message.release());
}
void NaClIPCAdapter::SaveMessage(const IPC::Message& msg,
RewrittenMessage* rewritten_msg) {
lock_.AssertAcquired();
NaClMessageHeader header;
memset(&header, 0, sizeof(NaClMessageHeader));
header.payload_size = static_cast<uint32>(msg.payload_size());
header.routing = msg.routing_id();
header.type = msg.type();
header.flags = msg.flags();
header.num_fds = static_cast<int>(rewritten_msg->desc_count());
rewritten_msg->SetData(header, msg.payload(), msg.payload_size());
locked_data_.to_be_received_.push(rewritten_msg);
}
int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
}