C++ bindings: A struct's Deserialize() now does validation before deserializing.
* Consequently, |Deserialize()| now has a |buf_size| argument.

BUG=#419
R=qsr@chromium.org, ukode@google.com, viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/1800753005 .
diff --git a/examples/serialization/main.cc b/examples/serialization/main.cc
index 9b0a96d..381ab97 100644
--- a/examples/serialization/main.cc
+++ b/examples/serialization/main.cc
@@ -19,8 +19,7 @@
 
   char buf[1000];
   MOJO_CHECK(in.Serialize(buf, sizeof(buf)));
-
-  out.Deserialize(buf);
+  MOJO_CHECK(out.Deserialize(buf, sizeof(buf)));
   MOJO_CHECK(out.a == 1);
   MOJO_CHECK(out.b == 2.0f);
   MOJO_CHECK(out.c == "hello world!");
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h
index 7889fa6..7479cd1 100644
--- a/mojo/public/cpp/bindings/lib/bindings_internal.h
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -98,6 +98,8 @@
   enum { value = std::is_base_of<Handle, H>::value };
 };
 
+// TODO(vardhan): Replace RemoveStructPtr<> and UnwrapStructPtr<> with
+// specializations of std::pointer_traits<> on [Inlined]StructPtr<>.
 template <typename T>
 struct RemoveStructPtr {
   typedef T type;
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
index c81fc6e..44158c2 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -17,14 +17,14 @@
 FixedBuffer::FixedBuffer() : ptr_(nullptr), cursor_(0), size_(0) {}
 
 void FixedBuffer::Initialize(void* memory, size_t size) {
-  MOJO_DCHECK(size == internal::Align(size));
-
   ptr_ = static_cast<char*>(memory);
   cursor_ = 0;
   size_ = size;
 }
 
 void* FixedBuffer::Allocate(size_t delta) {
+  // Ensure that all memory returned by Allocate() is 8-byte aligned w.r.t the
+  // start of the buffer.
   delta = internal::Align(delta);
 
   if (delta == 0 || delta > size_ - cursor_) {
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index 83eaf97..45d0411 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -44,9 +44,17 @@
 
   size_t size() const { return size_; }
 
+  // Returns the number of bytes used so far.
+  // TODO(vardhan): Introduce this method in |Buffer|? Doesn't seem necessary.
+  size_t BytesUsed() const { return cursor_; }
+
   // Grows the buffer by |num_bytes| and returns a pointer to the start of the
   // addition. The resulting address is 8-byte aligned, and the content of the
   // memory is zero-filled.
+  // TODO(vardhan): Allocate() should safely fail if we run out of buffer space.
+  // This will allow us to, e.g, fail when trying to consume a buffer to
+  // serialize into, and return an insufficient space error. Currently, there
+  // are consumers of FixedBuffer that rely on it CHECK-failing.
   void* Allocate(size_t num_bytes) override;
 
  protected:
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h
index d152333..da8fa3f 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.h
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -63,6 +63,7 @@
 
 const char* ValidationErrorToString(ValidationError error);
 
+// TODO(vardhan): This can die, along with |ValidationErrorObserverForTesting|.
 void ReportValidationError(ValidationError error,
                            std::string* description = nullptr);
 
diff --git a/mojo/public/cpp/bindings/tests/buffer_unittest.cc b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
index 317a2a5..96acdd9 100644
--- a/mojo/public/cpp/bindings/tests/buffer_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/buffer_unittest.cc
@@ -21,6 +21,14 @@
   return true;
 }
 
+// Tests that you can create a FixedBuffer whose underlying buffer size is not
+// a multiple of 8.
+TEST(FixedBufferTest, UnAlignedBufferSized) {
+  char char_buf[10] = {};
+  internal::FixedBuffer fixed_buf;
+  fixed_buf.Initialize(char_buf, sizeof(char_buf));
+}
+
 // Tests that FixedBuffer allocates memory aligned to 8 byte boundaries.
 TEST(FixedBufferTest, Alignment) {
   internal::FixedBufferForTesting buf(internal::Align(10) * 2);
diff --git a/mojo/public/cpp/bindings/tests/serialization_api_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_api_unittest.cc
index 28ada48..cd711dc 100644
--- a/mojo/public/cpp/bindings/tests/serialization_api_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/serialization_api_unittest.cc
@@ -23,25 +23,29 @@
   void SerializeAndDeserialize(
       Type* val,
       mojo::internal::ValidationError expected_validation_error) {
+    size_t bytes_written = 0;
     size_t num_bytes = val->GetSerializedSize();
     std::vector<uint8_t> bytes(num_bytes + 1);
 
     // Last byte is a magic value, helps catch a buffer overflow for
     // serialization.
     bytes[num_bytes] = 170;
-    val->Serialize(bytes.data(), num_bytes);
+    val->Serialize(bytes.data(), num_bytes, &bytes_written);
     EXPECT_EQ(170u, bytes[num_bytes]);
+    EXPECT_EQ(num_bytes, bytes_written);
 
     mojo::internal::BoundsChecker bounds_checker(bytes.data(), num_bytes, 0);
     auto actual_validation_error =
         Type::Data_::Validate(bytes.data(), &bounds_checker, nullptr);
     EXPECT_EQ(expected_validation_error, actual_validation_error);
 
+    Type out_val;
+    bool deserialize_ret = out_val.Deserialize(bytes.data(), bytes.size());
     if (actual_validation_error == mojo::internal::ValidationError::NONE) {
-      Type out_val;
-      out_val.Deserialize(bytes.data());
       EXPECT_TRUE(val->Equals(out_val));
     }
+    EXPECT_EQ(actual_validation_error == mojo::internal::ValidationError::NONE,
+              deserialize_ret);
   }
 
  private:
@@ -90,6 +94,23 @@
     SerializeAndDeserialize(&default_values,
                             mojo::internal::ValidationError::NONE);
   }
+
+  {
+    SCOPED_TRACE("NoDefaultFieldValues.Serialize() should fail");
+    NoDefaultFieldValues nd;
+    nd.f0 = true;
+    nd.f23 = mojo::Array<mojo::String>::New(10);
+
+    char buf[1000] = {};
+    EXPECT_FALSE(nd.Serialize(buf, sizeof(buf)));
+
+    size_t bytes_written = 0;
+    EXPECT_FALSE(nd.Serialize(buf, sizeof(buf), &bytes_written));
+    EXPECT_EQ(160UL, bytes_written);
+    // The Serialize() shouldn't get around to reserving space for the |f23|
+    // array field.
+    EXPECT_LT(bytes_written, nd.GetSerializedSize());
+  }
 }
 
 // This tests serialization of handles -- These should be deaths or
@@ -142,6 +163,46 @@
                           mojo::internal::ValidationError::NONE);
 }
 
+// Test that |Deserialize()| appropriately fails on validation.
+TEST_F(StructSerializationAPITest, DeserializationFailure) {
+  char buf[100] = {};
+  EmptyStruct es;
+
+  // Bounds checker should fail this, since buf_size is too small.
+  EXPECT_FALSE(es.Deserialize(buf, 1));
+
+  es.Serialize(buf, sizeof(buf));
+  EXPECT_TRUE(es.Deserialize(buf, sizeof(buf)));
+
+  // Invalid struct header: this should happen inside
+  // EmptyStruct::Data_::Validate()).
+  es.Serialize(buf, sizeof(buf));
+  EmptyStruct::Data_* es_data = reinterpret_cast<EmptyStruct::Data_*>(buf);
+  es_data->header_.num_bytes = 0;
+  EXPECT_FALSE(es.Deserialize(buf, sizeof(buf)));
+}
+
+TEST_F(StructSerializationAPITest, DeserializationWithoutValidation) {
+  const int32_t kMagic = 456;
+  char buf[100] = {};
+  ContainsOther::Data_* co_data = reinterpret_cast<ContainsOther::Data_*>(buf);
+  ContainsOther co;
+
+  // Success case.
+  co.other = kMagic;
+  EXPECT_TRUE(co.Serialize(buf, sizeof(buf)));
+  co.other = 0;
+  co.DeserializeWithoutValidation(buf);
+  EXPECT_EQ(kMagic, co.other);
+
+  // Invalid struct header, but will pass (i.e., won't crash) anyway because we
+  // don't Validate.
+  co_data->header_.num_bytes = 0u;
+  co.DeserializeWithoutValidation(buf);
+  EXPECT_EQ(kMagic, co_data->other);
+  EXPECT_EQ(0u, co_data->header_.num_bytes);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index afdfbbf..13f3b5e 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -663,8 +663,7 @@
   EXPECT_TRUE(small_struct->pod_union.is_null());
 
   SmallStructPtr deserialized_struct = SmallStruct::New();
-  deserialized_struct->Deserialize(buf);
-
+  EXPECT_TRUE(deserialized_struct->Deserialize(buf, sizeof(buf)));
   EXPECT_TRUE(deserialized_struct->pod_union.is_null());
 }
 
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
index 6ac6a3e..e80b405 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -5,34 +5,70 @@
 }
 
 bool {{struct.name}}::Serialize(void* buf,
-                                size_t buf_size) {
+                                size_t buf_size,
+                                size_t* bytes_written) {
+  MOJO_DCHECK(buf);
+
   mojo::internal::FixedBuffer overlay_buf;
   overlay_buf.Initialize(buf, buf_size);
 
   internal::{{struct.name}}_Data* output_ptr;
-  if (Serialize_(this, &overlay_buf, &output_ptr) !=
-      mojo::internal::ValidationError::NONE) {
+  auto err = Serialize_(this, &overlay_buf, &output_ptr);
+  if (err != mojo::internal::ValidationError::NONE) {
     // TODO(vardhan): Once Serialize_() outputs handles that it serialized
     // (even partially, if there are failures), we should CHECK fail here if
     // handles are non-empty.
+    MOJO_DLOG(ERROR) << "Could not serialize: " <<
+        mojo::internal::ValidationErrorToString(err);
+
+    if (bytes_written)
+      *bytes_written = overlay_buf.BytesUsed();
     return false;
   }
 
   std::vector<mojo::Handle> handles;
   output_ptr->EncodePointersAndHandles(&handles);
-
   MOJO_CHECK(handles.empty()) << "Serialize() does not support handles.";
 
+  if (bytes_written)
+    *bytes_written = overlay_buf.BytesUsed();
   return true;
 }
 
-void {{struct.name}}::Deserialize(void* buf) {
+bool {{struct.name}}::Deserialize(void* buf, size_t buf_size) {
+  MOJO_DCHECK(buf);
+
+  mojo::internal::BoundsChecker checker(buf, buf_size, 0);
+
+  std::string* err_str = nullptr;
+#if !defined(NDEBUG)
+  std::string err_str2;
+  err_str = &err_str2;
+#endif
+
+  mojo::internal::ValidationError err =
+      internal::{{struct.name}}_Data::Validate(buf, &checker, err_str);
+  if (err != mojo::internal::ValidationError::NONE) {
+    MOJO_DLOG(ERROR) << "Deserialization error "
+                     << mojo::internal::ValidationErrorToString(err)
+                     << ": " << *err_str;
+    return false;
+  }
+
+  DeserializeWithoutValidation(buf);
+  return true;
+}
+
+// TODO(vardhan): Make this |buf| a |const void*| once deserialization becomes
+// immutable.
+void {{struct.name}}::DeserializeWithoutValidation(void* buf) {
+  MOJO_DCHECK(buf);
+
   internal::{{struct.name}}_Data* input =
       static_cast<internal::{{struct.name}}_Data*>(buf);
-
   std::vector<mojo::Handle> handles;
   input->DecodePointersAndHandles(&handles);
-  MOJO_CHECK(handles.empty()) << "Deserialize() does not support handles.";
+  MOJO_CHECK(handles.empty()) << "Deserialization does not support handles.";
 
   Deserialize_(input, this);
 }
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
index 339f94f..1b976cf 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -35,28 +35,27 @@
   // Returns the number of bytes it would take to serialize this struct's data.
   size_t GetSerializedSize() const;
 
-  // Returns true on successful serialization.  On failure, part of the data may
+  // Returns true on successful serialization. On failure, part of the data may
   // be serialized, until the point of failure. This API does not support
-  // serializing handles.
+  // serializing handles. If not null, |bytes_written| is set to the number of
+  // bytes written to |buf|, even if this function return false. 
   //
   // TODO(vardhan): For now, we return true for success. Should we define a
-  // public error type for serialization? (we shouldn't reuse
-  // internal::ValidationError).
-  bool Serialize(void* buf, size_t buf_size);
+  // public error type for serialization? Should we open up
+  // internal::ValidationError?
+  bool Serialize(void* buf, size_t buf_size, size_t* bytes_written = nullptr);
+
+  // Deserializes the given |buf| of size |buf_size| representing a serialized
+  // version of this struct. The buffer is validated before it is deserialized.
+  // Returns true on successful deserialization.
+  // TODO(vardhan): Recover the validation error if there is one?
+  bool Deserialize(void* buf, size_t buf_size);
 
   // Deserializes the given |buf| representing a serialized version of this
-  // struct.  Assumes that the serialized |buf| is valid.
-  //
-  // TODO(vardhan):
-  //  - Should we pass in how big |buf| is and validate that it is
-  //    <= GetSerializedSize()? If so, should this validation happen all the
-  //    time?
-  //  - Deserialize() will CHECK-fail if you try to deserialize something with
-  //    a bad offset, etc.  For IPC, we |Validate()| before running
-  //    deserialization if we want safety, but we probably want a recoverable
-  //    error for this API.
-  //  - What's the validation story?
-  void Deserialize(void* buf);
+  // struct. The buffer is NOT validated before it is deserialized, so the user
+  // must be confident of its validity and that |buf| points to enough data to
+  // finish deserializing.
+  void DeserializeWithoutValidation(void* buf);
 
 {%  if struct|is_cloneable_kind %}
   {{struct.name}}Ptr Clone() const;
diff --git a/services/authentication/accounts_db_manager.cc b/services/authentication/accounts_db_manager.cc
index f7cd3fe..fe0a1ad 100644
--- a/services/authentication/accounts_db_manager.cc
+++ b/services/authentication/accounts_db_manager.cc
@@ -181,20 +181,11 @@
   if (bytes_read.size() != 0) {
     // Deserialize data from file
     const char* data = reinterpret_cast<const char*>(&bytes_read[0]);
-
-    // Validate the file contents before deserializing
-    mojo::internal::BoundsChecker bounds_checker(data, bytes_read.size(), 0);
-    std::string error;
-    mojo::internal::ValidationError verror =
-        internal::CredentialStore_Data::Validate(data, &bounds_checker, &error);
-    if (verror != mojo::internal::ValidationError::NONE) {
-      LOG(ERROR) << "Validation() error on accounts db ["
-                 << ValidationErrorToString(verror) << "][" << error << "]";
+    if (!creds_store_.Deserialize((void*)data, bytes_read.size())) {
+      LOG(ERROR) << "Deserialize() error on accounts db.";
       error_ = CREDENTIALS_DB_VALIDATE_ERROR;
       return;
     }
-
-    creds_store_.Deserialize((void*)data);
     // When we have multiple versions, this is not a fatal error, but a sign
     // that we need to update (or reinitialize) the db.
     CHECK_EQ(creds_store_.version, kCredsDbVersion);
@@ -219,17 +210,12 @@
   if (bytes_read.size() != 0) {
     // Deserialize data from file
     const char* data = reinterpret_cast<const char*>(&bytes_read[0]);
-
-    // Validate the file contents before deserializing
-    mojo::internal::BoundsChecker bounds_checker(data, bytes_read.size(), 0);
-    if (internal::Db_Data::Validate(data, &bounds_checker, nullptr) !=
-        mojo::internal::ValidationError::NONE) {
-      LOG(ERROR) << "Validation() error on auth db.";
+    if (!auth_grants_.Deserialize((void*)data, bytes_read.size())) {
+      LOG(ERROR) << "Deserialize() error on auth db.";
       error_ = AUTHORIZATIONS_DB_VALIDATE_ERROR;
       return;
     }
 
-    auth_grants_.Deserialize((void*)data);
     // When we have multiple versions, this is not a fatal error, but a sign
     // that we need to update (or reinitialize) the db.
     CHECK_EQ(auth_grants_.version, kAuthDbVersion);
diff --git a/services/url_response_disk_cache/url_response_disk_cache_db.cc b/services/url_response_disk_cache/url_response_disk_cache_db.cc
index fc06897..a96b34f 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_db.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_db.cc
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "leveldb/comparator.h"
 #include "leveldb/db.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
 #include "services/url_response_disk_cache/url_response_disk_cache_entry.mojom.h"
 
 namespace mojo {
@@ -26,40 +25,20 @@
 // character.
 const char kVersionKey[] = "\1version";
 
-// TODO(vardhan): Rework these to use the public Serialize/Deserialize
-// functions.
+// T is a |StructPtr|.
 template <typename T>
 void Serialize(T input, std::string* output) {
-  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
-  size_t size = GetSerializedSize_(*input);
-
+  size_t size = input->GetSerializedSize();
   output->clear();
   output->resize(size);
 
-  mojo::internal::FixedBuffer buf;
-  buf.Initialize(&output->at(0), size);
-
-  DataType data_type;
-  Serialize_(input.get(), &buf, &data_type);
-  std::vector<Handle> handles;
-  data_type->EncodePointersAndHandles(&handles);
+  input->Serialize(&output->at(0), size);
 }
 
 template <typename T>
 bool Deserialize(void* data, size_t size, T* output) {
-  typedef typename mojo::internal::WrapperTraits<T>::DataType DataType;
-  mojo::internal::BoundsChecker bounds_checker(data, size, 0);
-  if (std::remove_pointer<DataType>::type::Validate(data, &bounds_checker,
-                                                    nullptr) !=
-      mojo::internal::ValidationError::NONE)
-    return false;
-
-  DataType data_type = reinterpret_cast<DataType>(data);
-  std::vector<Handle> handles;
-  data_type->DecodePointersAndHandles(&handles);
   *output = mojo::internal::RemoveStructPtr<T>::type::New();
-  Deserialize_(data_type, output->get());
-  return true;
+  return (*output)->Deserialize(data, size);
 }
 
 template <typename T>