Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(690)

Unified Diff: ppapi/native_client/src/shared/ppapi_proxy/object_serialize.cc

Issue 7740013: Cloning a bunch of stuff from the native_client repository at r6528 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698