Commit 84eef4fa authored by zijiehe's avatar zijiehe Committed by Commit Bot

Support Serializing and Deserializing RepeatedField / RepeatedPtrField in IPC::Message

After this change, users can define a repeated field in protobuf,
message Message {
  repeated <type> foo;
}

and use
    GetParamSize(sizer, p.foo())
    WriteParam(m, p.foo())
and
    ReadParam(m, iter, r->mutable_foo())
to serialize and deserialize the field.

Serializing and deserializing a RepeatedField / RepeatedPtrField are needed in
change https://codereview.chromium.org/2964613002/.

BUG=650926

Review-Url: https://codereview.chromium.org/2968003005
Cr-Commit-Position: refs/heads/master@{#487593}
parent 30f6310f
......@@ -38,7 +38,6 @@ source_set("safe_browsing") {
"pe_image_reader_win.cc",
"pe_image_reader_win.h",
"protobuf_message_log_macros.h",
"protobuf_message_param_traits.h",
"protobuf_message_read_macros.h",
"protobuf_message_size_macros.h",
"protobuf_message_write_macros.h",
......
......@@ -5,8 +5,8 @@
// Multiply-included message file, so no include guard.
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_message_protobuf_utils.h"
#include "chrome/common/safe_browsing/ipc_protobuf_message_macros.h"
#include "chrome/common/safe_browsing/protobuf_message_param_traits.h"
IPC_PROTOBUF_MESSAGE_TRAITS_BEGIN(SubMessage)
IPC_PROTOBUF_MESSAGE_TRAITS_OPTIONAL_FUNDAMENTAL_MEMBER(foo)
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_SAFE_BROWSING_PROTOBUF_MESSAGE_PARAM_TRAITS_H_
#define CHROME_COMMON_SAFE_BROWSING_PROTOBUF_MESSAGE_PARAM_TRAITS_H_
#include <limits.h>
#include <string>
#include "base/pickle.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_param_traits.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
namespace IPC {
template <class Element>
struct ParamTraits<google::protobuf::RepeatedPtrField<Element>> {
typedef google::protobuf::RepeatedPtrField<Element> param_type;
static void GetSize(base::PickleSizer* s, const param_type& p) {
GetParamSize(s, p.size());
for (const auto& element : p)
GetParamSize(s, element);
}
static void Write(base::Pickle* m, const param_type& p) {
WriteParam(m, p.size());
for (const auto& element : p)
WriteParam(m, element);
}
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* p) {
int size;
if (!iter->ReadLength(&size) || size < 0)
return false;
if (INT_MAX / static_cast<int>(sizeof(void*)) <= size)
return false;
p->Clear();
p->Reserve(size);
while (size--) {
if (!ReadParam(m, iter, p->Add()))
return false;
}
return true;
}
static void Log(const param_type& p, std::string* l) {
bool logged_first = false;
for (const auto& element : p) {
if (logged_first)
l->push_back(' ');
else
logged_first = true;
LogParam(element, l);
}
}
};
} // namespace IPC
#endif // CHROME_COMMON_SAFE_BROWSING_PROTOBUF_MESSAGE_PARAM_TRAITS_H_
......@@ -11,8 +11,8 @@
#include "build/build_config.h"
#include "chrome/common/safe_browsing/archive_analyzer_results.h"
#include "chrome/common/safe_browsing/ipc_protobuf_message_macros.h"
#include "chrome/common/safe_browsing/protobuf_message_param_traits.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_message_protobuf_utils.h"
IPC_ENUM_TRAITS_VALIDATE(
safe_browsing::ClientDownloadRequest_DownloadType,
......
......@@ -7,6 +7,7 @@ import("//build/config/nacl/config.gni")
import("//mojo/public/tools/bindings/mojom.gni")
import("//testing/test.gni")
import("//tools/ipc_fuzzer/ipc_fuzzer.gni")
import("//third_party/protobuf/proto_library.gni")
declare_args() {
# Enabling debug builds automatically sets enable_ipc_logging to true.
......@@ -122,6 +123,17 @@ component("ipc") {
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
]
if (!is_nacl_nonsfi) {
sources += [
"ipc_message_protobuf_utils.h",
]
public_deps += [
"//third_party/protobuf:protobuf_lite",
]
}
deps = [
"//base",
]
......@@ -177,6 +189,12 @@ if (!is_ios) {
]
}
proto_library("test_proto") {
sources = [
"test_proto.proto",
]
}
test("ipc_tests") {
sources = [
"ipc_channel_mojo_unittest.cc",
......@@ -204,6 +222,7 @@ if (!is_ios) {
":ipc",
":run_all_unittests",
":test_interfaces",
":test_proto",
":test_support",
"//base",
"//base:i18n",
......@@ -224,6 +243,12 @@ if (!is_ios) {
"sync_socket_unittest.cc",
]
}
if (!is_nacl_nonsfi) {
sources += [
"ipc_message_protobuf_utils_unittest.cc",
]
}
}
test("ipc_perftests") {
......
......@@ -20,5 +20,9 @@ specific_include_rules = {
"run_all_(unit|perf)tests\.cc": [
"+mojo/edk/embedder",
"+mojo/edk/test",
]
],
"ipc_message_protobuf_utils\.h": [
# Support serializing RepeatedField / RepeatedPtrField:
"+third_party/protobuf/src/google/protobuf/repeated_field.h",
],
}
......@@ -126,6 +126,20 @@ TEST(IPCMessageIntegrity, ReadVectorTooLarge2) {
EXPECT_FALSE(ReadParam(&m, &iter, &vec));
}
// This test needs ~20 seconds in Debug mode, or ~4 seconds in Release mode.
// See http://crbug.com/741866 for details.
TEST(IPCMessageIntegrity, DISABLED_ReadVectorTooLarge3) {
base::Pickle pickle;
IPC::WriteParam(&pickle, 256 * 1024 * 1024);
IPC::WriteParam(&pickle, 0);
IPC::WriteParam(&pickle, 1);
IPC::WriteParam(&pickle, 2);
base::PickleIterator iter(pickle);
std::vector<int> vec;
EXPECT_FALSE(IPC::ReadParam(&pickle, &iter, &vec));
}
class SimpleListener : public IPC::Listener {
public:
SimpleListener() : other_(NULL) {
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IPC_IPC_MESSAGE_PROTOBUF_UTILS_H_
#define IPC_IPC_MESSAGE_PROTOBUF_UTILS_H_
#include "build/build_config.h"
#if defined(OS_NACL_NONSFI)
static_assert(false,
"ipc_message_protobuf_utils is not able to work with "
"nacl_nonsfi configuration.");
#endif
#include "base/pickle.h"
#include "ipc/ipc_param_traits.h"
#include "ipc/ipc_message_utils.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
namespace IPC {
template <class RepeatedFieldLike, class StorageType>
struct RepeatedFieldParamTraits {
typedef RepeatedFieldLike param_type;
static void GetSize(base::PickleSizer* sizer, const param_type& p) {
GetParamSize(sizer, p.size());
for (int i = 0; i < p.size(); i++)
GetParamSize(sizer, p.Get(i));
}
static void Write(base::Pickle* m, const param_type& p) {
WriteParam(m, p.size());
for (int i = 0; i < p.size(); i++)
WriteParam(m, p.Get(i));
}
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
int size;
// ReadLength() checks for < 0 itself.
if (!iter->ReadLength(&size))
return false;
// Avoid integer overflow / assertion failure in Reserve() function.
if (INT_MAX / sizeof(StorageType) <= static_cast<size_t>(size))
return false;
r->Reserve(size);
for (int i = 0; i < size; i++) {
if (!ReadParam(m, iter, r->Add()))
return false;
}
return true;
}
static void Log(const param_type& p, std::string* l) {
for (int i = 0; i < p.size(); ++i) {
if (i != 0)
l->append(" ");
LogParam(p.Get(i), l);
}
}
};
template <class P>
struct ParamTraits<google::protobuf::RepeatedField<P>> :
RepeatedFieldParamTraits<google::protobuf::RepeatedField<P>, P> {};
template <class P>
struct ParamTraits<google::protobuf::RepeatedPtrField<P>> :
RepeatedFieldParamTraits<google::protobuf::RepeatedPtrField<P>, void*> {};
} // namespace IPC
#endif // IPC_IPC_MESSAGE_PROTOBUF_UTILS_H_
// Copyright 2017 The Chromium 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 "build/build_config.h"
#if defined(OS_NACL_NONSFI)
static_assert(false,
"ipc_message_protobuf_utils is not able to work with nacl_nonsfi "
"configuration.");
#endif
#include "ipc/ipc_message_protobuf_utils.h"
#include <initializer_list>
#include "ipc/test_proto.pb.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace IPC {
template <>
struct ParamTraits<ipc_message_utils_test::TestMessage1> {
typedef ipc_message_utils_test::TestMessage1 param_type;
static void GetSize(base::PickleSizer* sizer, const param_type& p) {
GetParamSize(sizer, p.number());
}
static void Write(base::Pickle* m, const param_type& p) {
WriteParam(m, p.number());
}
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
int number;
if (!iter->ReadInt(&number))
return false;
r->set_number(number);
return true;
}
};
template <>
struct ParamTraits<ipc_message_utils_test::TestMessage2> {
typedef ipc_message_utils_test::TestMessage2 param_type;
static void GetSize(base::PickleSizer* sizer, const param_type& p) {
GetParamSize(sizer, p.numbers());
GetParamSize(sizer, p.strings());
GetParamSize(sizer, p.messages());
}
static void Write(base::Pickle* m, const param_type& p) {
WriteParam(m, p.numbers());
WriteParam(m, p.strings());
WriteParam(m, p.messages());
}
static bool Read(const base::Pickle* m,
base::PickleIterator* iter,
param_type* r) {
return ReadParam(m, iter, r->mutable_numbers()) &&
ReadParam(m, iter, r->mutable_strings()) &&
ReadParam(m, iter, r->mutable_messages());
}
};
namespace {
template <class P1, class P2>
void AssertEqual(const P1& left, const P2& right) {
ASSERT_EQ(left, right);
}
template<>
void AssertEqual(const int& left,
const ipc_message_utils_test::TestMessage1& right) {
ASSERT_EQ(left, right.number());
}
template <template<class> class RepeatedFieldLike, class P1, class P2>
void AssertRepeatedFieldEquals(std::initializer_list<P1> expected,
const RepeatedFieldLike<P2>& fields) {
ASSERT_EQ(static_cast<int>(expected.size()), fields.size());
auto it = expected.begin();
int i = 0;
for (; it != expected.end(); it++, i++) {
AssertEqual(*it, fields.Get(i));
}
}
TEST(IPCMessageRepeatedFieldUtilsTest, RepeatedFieldShouldBeSerialized) {
ipc_message_utils_test::TestMessage2 message;
message.add_numbers(1);
message.add_numbers(100);
message.add_strings("abc");
message.add_strings("def");
message.add_messages()->set_number(1000);
message.add_messages()->set_number(10000);
base::Pickle pickle;
IPC::WriteParam(&pickle, message);
base::PickleSizer sizer;
IPC::GetParamSize(&sizer, message);
ASSERT_EQ(sizer.payload_size(), pickle.payload_size());
base::PickleIterator iter(pickle);
ipc_message_utils_test::TestMessage2 output;
ASSERT_TRUE(IPC::ReadParam(&pickle, &iter, &output));
AssertRepeatedFieldEquals({1, 100}, output.numbers());
AssertRepeatedFieldEquals({"abc", "def"}, output.strings());
AssertRepeatedFieldEquals({1000, 10000}, output.messages());
}
TEST(IPCMessageRepeatedFieldUtilsTest,
PartialEmptyRepeatedFieldShouldBeSerialized) {
ipc_message_utils_test::TestMessage2 message;
message.add_numbers(1);
message.add_numbers(100);
message.add_messages()->set_number(1000);
message.add_messages()->set_number(10000);
base::Pickle pickle;
IPC::WriteParam(&pickle, message);
base::PickleSizer sizer;
IPC::GetParamSize(&sizer, message);
ASSERT_EQ(sizer.payload_size(), pickle.payload_size());
base::PickleIterator iter(pickle);
ipc_message_utils_test::TestMessage2 output;
ASSERT_TRUE(IPC::ReadParam(&pickle, &iter, &output));
AssertRepeatedFieldEquals({1, 100}, output.numbers());
ASSERT_EQ(0, output.strings_size());
AssertRepeatedFieldEquals({1000, 10000}, output.messages());
}
TEST(IPCMessageRepeatedFieldUtilsTest, EmptyRepeatedFieldShouldBeSerialized) {
ipc_message_utils_test::TestMessage2 message;
base::Pickle pickle;
IPC::WriteParam(&pickle, message);
base::PickleSizer sizer;
IPC::GetParamSize(&sizer, message);
ASSERT_EQ(sizer.payload_size(), pickle.payload_size());
base::PickleIterator iter(pickle);
ipc_message_utils_test::TestMessage2 output;
ASSERT_TRUE(IPC::ReadParam(&pickle, &iter, &output));
ASSERT_EQ(0, output.numbers_size());
ASSERT_EQ(0, output.strings_size());
ASSERT_EQ(0, output.messages_size());
}
TEST(IPCMessageRepeatedFieldUtilsTest,
InvalidPickleShouldNotCrashRepeatedFieldDeserialization) {
base::Pickle pickle;
IPC::WriteParam(&pickle, INT_MAX);
IPC::WriteParam(&pickle, 0);
IPC::WriteParam(&pickle, INT_MAX);
IPC::WriteParam(&pickle, std::string());
IPC::WriteParam(&pickle, 0);
base::PickleIterator iter(pickle);
ipc_message_utils_test::TestMessage2 output;
ASSERT_FALSE(IPC::ReadParam(&pickle, &iter, &output));
}
// This test needs ~20 seconds in Debug mode, or ~4 seconds in Release mode.
// See http://crbug.com/741866 for details.
TEST(IPCMessageRepeatedFieldUtilsTest,
DISABLED_InvalidPickleShouldNotCrashRepeatedFieldDeserialization2) {
base::Pickle pickle;
IPC::WriteParam(&pickle, 256 * 1024 * 1024);
IPC::WriteParam(&pickle, 0);
IPC::WriteParam(&pickle, INT_MAX);
IPC::WriteParam(&pickle, std::string());
IPC::WriteParam(&pickle, 0);
base::PickleIterator iter(pickle);
ipc_message_utils_test::TestMessage2 output;
ASSERT_FALSE(IPC::ReadParam(&pickle, &iter, &output));
}
} // namespace
} // namespace IPC
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package ipc_message_utils_test;
// This is a simple dummy protocol buffer that is used for testing handling of
// protocol buffers in ipc_message_utils.
message TestMessage1 {
optional int32 number = 1;
}
message TestMessage2 {
repeated int32 numbers = 1;
repeated string strings = 2;
repeated TestMessage1 messages = 3;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment