| Index: ppapi/native_client/src/shared/ppapi_proxy/object_serialize.cc
|
| ===================================================================
|
| --- ppapi/native_client/src/shared/ppapi_proxy/object_serialize.cc (revision 0)
|
| +++ ppapi/native_client/src/shared/ppapi_proxy/object_serialize.cc (revision 0)
|
| @@ -0,0 +1,468 @@
|
| +/*
|
| + * Copyright (c) 2011 The Native Client Authors. All rights reserved.
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "native_client/src/shared/ppapi_proxy/object_serialize.h"
|
| +
|
| +#include <limits>
|
| +#include <stdio.h>
|
| +#include <string.h>
|
| +
|
| +
|
| +#include "native_client/src/include/nacl_macros.h"
|
| +#include "native_client/src/include/portability_process.h"
|
| +#ifdef __native_client__
|
| +#include "native_client/src/shared/ppapi_proxy/plugin_globals.h"
|
| +#else
|
| +#include "native_client/src/shared/ppapi_proxy/browser_globals.h"
|
| +#endif // __native_client__
|
| +#include "native_client/src/shared/ppapi_proxy/utility.h"
|
| +#include "ppapi/c/pp_bool.h"
|
| +#include "ppapi/c/pp_var.h"
|
| +
|
| +namespace ppapi_proxy {
|
| +
|
| +namespace {
|
| +
|
| +// A serialized string consists of a fixed minimum of 8 bytes.
|
| +static const int kStringFixedBytes = 8;
|
| +// Followed by a varying number of bytes rounded up to the nearest 8 bytes.
|
| +static const uint32_t kStringRoundBase = 8;
|
| +
|
| +} // namespace
|
| +
|
| +// The basic serialization structure. Used alone for PP_VARTYPE_VOID,
|
| +// PP_VARTYPE_NULL, and PP_VARTYPE_INT32.
|
| +struct SerializedFixed {
|
| + uint32_t type;
|
| + union {
|
| + // PP_VARTYPE_BOOLEAN uses this.
|
| + bool boolean_value;
|
| + // PP_VARTYPE_INT32 uses this.
|
| + int32_t int32_value;
|
| + // PP_VARTYPE_STRING uses this.
|
| + uint32_t string_length;
|
| + } u;
|
| + // The size of this structure should be 8 bytes on all platforms.
|
| +};
|
| +
|
| +// The structure used for PP_VARTYPE_DOUBLE.
|
| +struct SerializedDouble {
|
| + struct SerializedFixed fixed;
|
| + double double_value;
|
| +};
|
| +
|
| +// The structure used for PP_VARTYPE_STRING.
|
| +
|
| +struct SerializedString {
|
| + struct SerializedFixed fixed;
|
| + char string_bytes[kStringFixedBytes];
|
| + // Any remaining characters immediately follow, and are padded out to the
|
| + // nearest multiple of kStringRoundBase bytes.
|
| +};
|
| +
|
| +// TODO(sehr): Add a more general compile time assertion package elsewhere.
|
| +#define ASSERT_TYPE_SIZE(struct_name, struct_size) \
|
| + int struct_name##_size_should_be_##struct_size[ \
|
| + sizeof(struct_name) == struct_size ? 1 : 0]
|
| +
|
| +// Check the wire format sizes for the PP_Var subtypes.
|
| +ASSERT_TYPE_SIZE(SerializedFixed, 8);
|
| +ASSERT_TYPE_SIZE(SerializedDouble, 16);
|
| +ASSERT_TYPE_SIZE(SerializedString, 16);
|
| +
|
| +//
|
| +// We currently use offsetof to find the start of string storage.
|
| +// This avoids the (never seen) case where the compiler inserts in
|
| +// padding between the struct SerializedFixed fixed header and the
|
| +// actual payload value in the double, string, and object
|
| +// serialization variants.
|
| +//
|
| +// Untrusted arm toolchain defines an offsetof in stddef.h, so we have
|
| +// to prefix.
|
| +//
|
| +#define NACL_OFFSETOF(pod_t, member) \
|
| + (static_cast<size_t>(reinterpret_cast<uintptr_t>(&((pod_t *) NULL)->member)))
|
| +
|
| +namespace {
|
| +
|
| +// Adding value1 and value2 would overflow a uint32_t.
|
| +bool AddWouldOverflow(size_t value1, size_t value2) {
|
| + if (value1 > std::numeric_limits<size_t>::max() - value2) {
|
| + return true;
|
| + }
|
| + size_t sum = value1 + value2;
|
| + return sum > std::numeric_limits<uint32_t>::max();
|
| +}
|
| +
|
| +uint32_t RoundedStringBytes(uint32_t string_length) {
|
| + // Compute the string length, padded to the nearest multiple of 8.
|
| + if (AddWouldOverflow(string_length, kStringRoundBase - 1)) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + return (string_length + (kStringRoundBase - 1)) & ~(kStringRoundBase - 1);
|
| +}
|
| +
|
| +uint32_t PpVarSize(const PP_Var& var) {
|
| + switch (var.type) {
|
| + case PP_VARTYPE_UNDEFINED:
|
| + case PP_VARTYPE_NULL:
|
| + case PP_VARTYPE_BOOL:
|
| + case PP_VARTYPE_INT32:
|
| + return sizeof(SerializedFixed);
|
| + case PP_VARTYPE_DOUBLE:
|
| + return sizeof(SerializedDouble);
|
| + case PP_VARTYPE_STRING: {
|
| + uint32_t string_length;
|
| + (void) PPBVarInterface()->VarToUtf8(var, &string_length);
|
| + string_length = RoundedStringBytes(string_length);
|
| + if (std::numeric_limits<uint32_t>::max() == string_length ||
|
| + AddWouldOverflow(string_length,
|
| + NACL_OFFSETOF(SerializedString, string_bytes))) {
|
| + // Adding the length to the fixed portion would overflow.
|
| + return 0;
|
| + }
|
| + return static_cast<uint32_t>(NACL_OFFSETOF(SerializedString, string_bytes)
|
| + + string_length);
|
| + break;
|
| + }
|
| + case PP_VARTYPE_OBJECT:
|
| + case PP_VARTYPE_ARRAY:
|
| + case PP_VARTYPE_DICTIONARY:
|
| + NACL_NOTREACHED();
|
| + break;
|
| + }
|
| + // Unrecognized type.
|
| + return 0;
|
| +}
|
| +
|
| +uint32_t PpVarVectorSize(const PP_Var* vars, uint32_t argc) {
|
| + size_t size = 0;
|
| +
|
| + for (uint32_t i = 0; i < argc; ++i) {
|
| + size_t element_size = PpVarSize(vars[i]);
|
| +
|
| + if (0 == element_size || AddWouldOverflow(size, element_size)) {
|
| + // Overflow.
|
| + return 0;
|
| + }
|
| + size += element_size;
|
| + }
|
| + return static_cast<uint32_t>(size);
|
| +}
|
| +
|
| +bool SerializePpVar(const PP_Var* vars,
|
| + uint32_t argc,
|
| + char* bytes,
|
| + uint32_t length) {
|
| + size_t offset = 0;
|
| +
|
| + for (uint32_t i = 0; i < argc; ++i) {
|
| + size_t element_size = PpVarSize(vars[i]);
|
| + if (0 == element_size || AddWouldOverflow(offset, element_size)) {
|
| + // Overflow.
|
| + return false;
|
| + }
|
| + if (offset + element_size > length) {
|
| + // Not enough bytes to put the requested number of PP_Vars.
|
| + return false;
|
| + }
|
| +
|
| + char* p = bytes + offset;
|
| + SerializedFixed* s = reinterpret_cast<SerializedFixed*>(p);
|
| + s->type = static_cast<uint32_t>(vars[i].type);
|
| + // Set the rest of SerializedFixed to 0, in case the following serialization
|
| + // leaves some of it unchanged.
|
| + s->u.int32_value = 0;
|
| +
|
| + switch (vars[i].type) {
|
| + case PP_VARTYPE_UNDEFINED:
|
| + case PP_VARTYPE_NULL:
|
| + element_size = sizeof(SerializedFixed);
|
| + break;
|
| + case PP_VARTYPE_BOOL:
|
| + s->u.boolean_value = static_cast<bool>
|
| + (PP_TRUE == vars[i].value.as_bool);
|
| + element_size = sizeof(SerializedFixed);
|
| + break;
|
| + case PP_VARTYPE_INT32:
|
| + s->u.int32_value = vars[i].value.as_int;
|
| + element_size = sizeof(SerializedFixed);
|
| + break;
|
| + case PP_VARTYPE_DOUBLE: {
|
| + SerializedDouble* sd = reinterpret_cast<SerializedDouble*>(p);
|
| + sd->double_value = vars[i].value.as_double;
|
| + element_size = sizeof(SerializedDouble);
|
| + break;
|
| + }
|
| + case PP_VARTYPE_STRING: {
|
| + uint32_t string_length;
|
| + const char* str = PPBVarInterface()->VarToUtf8(vars[i], &string_length);
|
| + SerializedString* ss = reinterpret_cast<SerializedString*>(p);
|
| + ss->fixed.u.string_length = string_length;
|
| + memcpy(reinterpret_cast<void*>(ss->string_bytes),
|
| + reinterpret_cast<const void*>(str),
|
| + string_length);
|
| + // Fill padding bytes with zeros.
|
| + memset(reinterpret_cast<void*>(ss->string_bytes + string_length), 0,
|
| + RoundedStringBytes(string_length) - string_length);
|
| + element_size = NACL_OFFSETOF(SerializedString, string_bytes)
|
| + + RoundedStringBytes(string_length);
|
| + break;
|
| + }
|
| + case PP_VARTYPE_OBJECT:
|
| + case PP_VARTYPE_ARRAY:
|
| + case PP_VARTYPE_DICTIONARY:
|
| + NACL_NOTREACHED();
|
| + default:
|
| + return false;
|
| + }
|
| + offset += element_size;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +//
|
| +// Compute how many bytes does the string object to be deserialzed use
|
| +// in the serialized format. On error, return
|
| +// std::numeric_limits<uint32_t>::max(). This means we cannot handle
|
| +// 2**32-1 byte strings.
|
| +//
|
| +uint32_t DeserializeStringSize(char* p, uint32_t length) {
|
| + // zero length strings are okay... but not shorter
|
| + if (length < NACL_OFFSETOF(SerializedString, string_bytes)) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + SerializedString* ss = reinterpret_cast<SerializedString*>(p);
|
| + if (PP_VARTYPE_STRING != ss->fixed.type) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + uint32_t string_length = ss->fixed.u.string_length;
|
| + string_length = RoundedStringBytes(string_length);
|
| + if (std::numeric_limits<uint32_t>::max() == string_length) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + if (AddWouldOverflow(NACL_OFFSETOF(SerializedString, string_bytes),
|
| + string_length)) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + uint32_t total_bytes = NACL_OFFSETOF(SerializedString, string_bytes)
|
| + + string_length;
|
| + if (total_bytes > length) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + return total_bytes;
|
| +}
|
| +
|
| +
|
| +//
|
| +// Compute the number of bytes that will be consumed by the next
|
| +// object, based on its type. If there aren't enough bytes,
|
| +// std::numeric_limits<uint32_t>::max() will be returned.
|
| +//
|
| +// If element_type_ptr is non-NULL, then the next element's
|
| +// (purported) type will be filled in. Whether this occurs when there
|
| +// is an error (e.g., not enough data) is not defined, i.e., only rely
|
| +// on it when there's no error.
|
| +//
|
| +uint32_t DeserializePpVarSize(char* p,
|
| + uint32_t length,
|
| + PP_VarType* element_type_ptr) {
|
| + SerializedFixed* sfp;
|
| + if (length < sizeof *sfp) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + sfp = reinterpret_cast<SerializedFixed*>(p);
|
| + uint32_t expected_element_size = 0;
|
| + //
|
| + // Setting this to zero handles the "default" case. That can occur
|
| + // because sfp->type can originate from untrusted code, and so the
|
| + // value could actually be outside of the PP_VarType enumeration
|
| + // range. If we hit one of the cases below, then
|
| + // expected_element_size will be bounded away from zero.
|
| + //
|
| + switch (static_cast<PP_VarType>(sfp->type)) {
|
| + case PP_VARTYPE_UNDEFINED:
|
| + case PP_VARTYPE_NULL:
|
| + case PP_VARTYPE_BOOL:
|
| + case PP_VARTYPE_INT32:
|
| + expected_element_size = sizeof(SerializedFixed);
|
| + break;
|
| + case PP_VARTYPE_DOUBLE:
|
| + expected_element_size = sizeof(SerializedDouble);
|
| + break;
|
| + case PP_VARTYPE_STRING:
|
| + expected_element_size = DeserializeStringSize(p, length);
|
| + if (std::numeric_limits<uint32_t>::max() == expected_element_size) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + break;
|
| + // NB: No default case to trigger -Wswitch-enum, so changes to
|
| + // PP_VarType w/o corresponding changes here will cause a
|
| + // compile-time error.
|
| + case PP_VARTYPE_OBJECT:
|
| + case PP_VARTYPE_ARRAY:
|
| + case PP_VARTYPE_DICTIONARY:
|
| + NACL_NOTREACHED();
|
| + break;
|
| + }
|
| + if (length < expected_element_size) {
|
| + return std::numeric_limits<uint32_t>::max();
|
| + }
|
| + if (NULL != element_type_ptr) {
|
| + *element_type_ptr = static_cast<PP_VarType>(sfp->type);
|
| + }
|
| + return expected_element_size;
|
| +}
|
| +
|
| +
|
| +//
|
| +// This should be invoked only if DeserializePpVarSize succeeds, i.e.,
|
| +// there are enough bytes at p.
|
| +//
|
| +bool DeserializeString(char* p,
|
| + PP_Var* var,
|
| + NaClSrpcChannel* channel) {
|
| + SerializedString* ss = reinterpret_cast<SerializedString*>(p);
|
| + uint32_t string_length = ss->fixed.u.string_length;
|
| + // VarFromUtf8 creates a buffer of size string_length using the browser-side
|
| + // memory allocation function, and copies string_length bytes from
|
| + // ss->string_bytes in to that buffer. The ref count of the returned var is
|
| + // 1.
|
| + *var = PPBVarInterface()->VarFromUtf8(LookupModuleIdForSrpcChannel(channel),
|
| + ss->string_bytes,
|
| + string_length);
|
| + return true;
|
| +}
|
| +
|
| +bool DeserializePpVar(NaClSrpcChannel* channel,
|
| + char* bytes,
|
| + uint32_t length,
|
| + PP_Var* vars,
|
| + uint32_t argc) {
|
| + char* p = bytes;
|
| +
|
| + for (uint32_t i = 0; i < argc; ++i) {
|
| + PP_VarType element_type;
|
| + uint32_t element_size = DeserializePpVarSize(p, length, &element_type);
|
| + if (std::numeric_limits<uint32_t>::max() == element_size) {
|
| + return false;
|
| + }
|
| + SerializedFixed* s = reinterpret_cast<SerializedFixed*>(p);
|
| +
|
| + vars[i].type = element_type;
|
| + switch (element_type) {
|
| + case PP_VARTYPE_UNDEFINED:
|
| + case PP_VARTYPE_NULL:
|
| + break;
|
| + case PP_VARTYPE_BOOL:
|
| + vars[i].value.as_bool = static_cast<PP_Bool>(s->u.boolean_value);
|
| + break;
|
| + case PP_VARTYPE_INT32:
|
| + vars[i].value.as_int = s->u.int32_value;
|
| + break;
|
| + case PP_VARTYPE_DOUBLE: {
|
| + SerializedDouble* sd = reinterpret_cast<SerializedDouble*>(p);
|
| + vars[i].value.as_double = sd->double_value;
|
| + break;
|
| + }
|
| + case PP_VARTYPE_STRING:
|
| + if (!DeserializeString(p, &vars[i], channel)) {
|
| + return false;
|
| + }
|
| + break;
|
| + case PP_VARTYPE_OBJECT:
|
| + case PP_VARTYPE_ARRAY:
|
| + case PP_VARTYPE_DICTIONARY:
|
| + NACL_NOTREACHED();
|
| + default:
|
| + return false;
|
| + }
|
| + p += element_size;
|
| + length -= element_size;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool SerializeTo(const PP_Var* var, char* bytes, uint32_t* length) {
|
| + if (bytes == NULL || length == NULL) {
|
| + return false;
|
| + }
|
| + // Compute the size of the serialized form. Zero indicates error.
|
| + uint32_t tmp_length = PpVarVectorSize(var, 1);
|
| + if (0 == tmp_length || tmp_length > *length) {
|
| + return false;
|
| + }
|
| + // Serialize the var.
|
| + if (!SerializePpVar(var, 1, bytes, tmp_length)) {
|
| + return false;
|
| + }
|
| + // Return success.
|
| + *length = tmp_length;
|
| + return true;
|
| +}
|
| +
|
| +char* Serialize(const PP_Var* vars, uint32_t argc, uint32_t* length) {
|
| + // Length needs to be set.
|
| + if (NULL == length) {
|
| + return NULL;
|
| + }
|
| + // No need to do anything if there are no vars to serialize.
|
| + if (0 == argc) {
|
| + *length = 0;
|
| + return NULL;
|
| + }
|
| + // Report an error if no vars are passed but argc > 0.
|
| + if (NULL == vars) {
|
| + return NULL;
|
| + }
|
| + // Compute the size of the buffer. Zero indicates error.
|
| + uint32_t tmp_length = PpVarVectorSize(vars, argc);
|
| + if (0 == tmp_length || tmp_length > *length) {
|
| + return NULL;
|
| + }
|
| + // Allocate the buffer, if the client didn't pass one.
|
| + char* bytes = new char[tmp_length];
|
| + if (NULL == bytes) {
|
| + return NULL;
|
| + }
|
| + // Serialize the vars.
|
| + if (!SerializePpVar(vars, argc, bytes, tmp_length)) {
|
| + delete[] bytes;
|
| + return NULL;
|
| + }
|
| + // Return success.
|
| + *length = tmp_length;
|
| + return bytes;
|
| +}
|
| +
|
| +bool DeserializeTo(NaClSrpcChannel* channel,
|
| + char* bytes,
|
| + uint32_t length,
|
| + uint32_t argc,
|
| + PP_Var* vars) {
|
| + // Deserializing a zero-length vector is trivially done.
|
| + if (0 == argc) {
|
| + return true;
|
| + }
|
| + // Otherwise, there must be some input bytes to get from.
|
| + if (NULL == bytes || 0 == length) {
|
| + return false;
|
| + }
|
| + // And there has to be a valid address to deserialize to.
|
| + if (NULL == vars) {
|
| + return false;
|
| + }
|
| + // Read the serialized PP_Vars into the allocated memory.
|
| + if (!DeserializePpVar(channel, bytes, length, vars, argc)) {
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace ppapi_proxy
|
|
|