Commit aca9a440 authored by Ken Rockot's avatar Ken Rockot Committed by Commit Bot

[mojom] Introduce [UnlimitedSize] attribute

This adds support for a new [UnlimitedSize] attribute on mojom
interface methods, allowing them to bypass size-checking constraints in
cases where the possibility of an occasional very large message is a
known issue and is either awaiting a fix or is deemed acceptable as-is.

Bug: None
Change-Id: I6a2f884c71bf912e8804ed40afd22132b4d8e969
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2368429
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800718}
parent a28bdd96
......@@ -343,8 +343,10 @@ MojoResult Core::CreateMessage(const MojoCreateMessageOptions* options,
return MOJO_RESULT_INVALID_ARGUMENT;
if (options && options->struct_size < sizeof(*options))
return MOJO_RESULT_INVALID_ARGUMENT;
const MojoCreateMessageFlags flags =
options ? options->flags : MOJO_CREATE_MESSAGE_FLAG_NONE;
*message_handle = reinterpret_cast<MojoMessageHandle>(
UserMessageImpl::CreateEventForNewMessage().release());
UserMessageImpl::CreateEventForNewMessage(flags).release());
return MOJO_RESULT_OK;
}
......
......@@ -344,10 +344,10 @@ UserMessageImpl::~UserMessageImpl() {
// static
std::unique_ptr<ports::UserMessageEvent>
UserMessageImpl::CreateEventForNewMessage() {
UserMessageImpl::CreateEventForNewMessage(MojoCreateMessageFlags flags) {
auto message_event = std::make_unique<ports::UserMessageEvent>(0);
message_event->AttachMessage(
base::WrapUnique(new UserMessageImpl(message_event.get())));
base::WrapUnique(new UserMessageImpl(message_event.get(), flags)));
return message_event;
}
......@@ -535,7 +535,8 @@ MojoResult UserMessageImpl::CommitSize() {
pending_handle_attachments_.clear();
}
if (user_payload_size_ > GetConfiguration().max_message_num_bytes) {
if (!unlimited_size_ &&
user_payload_size_ > GetConfiguration().max_message_num_bytes) {
// We want to be aware of new undocumented cases of very large IPCs. Crashes
// which result from this stack should be addressed by either marking the
// corresponding mojom interface method with an [UnlimitedSize] attribute;
......@@ -664,8 +665,11 @@ void UserMessageImpl::FailHandleSerializationForTesting(bool fail) {
g_always_fail_handle_serialization = fail;
}
UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event)
: ports::UserMessage(&kUserMessageTypeInfo), message_event_(message_event) {
UserMessageImpl::UserMessageImpl(ports::UserMessageEvent* message_event,
MojoCreateMessageFlags flags)
: ports::UserMessage(&kUserMessageTypeInfo),
message_event_(message_event),
unlimited_size_((flags & MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE) != 0) {
EnsureMemoryDumpProviderExists();
IncrementMessageCount();
}
......
......@@ -49,7 +49,8 @@ class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage {
~UserMessageImpl() override;
// Creates a new ports::UserMessageEvent with an attached UserMessageImpl.
static std::unique_ptr<ports::UserMessageEvent> CreateEventForNewMessage();
static std::unique_ptr<ports::UserMessageEvent> CreateEventForNewMessage(
MojoCreateMessageFlags flags);
// Creates a new ports::UserMessageEvent with an attached serialized
// UserMessageImpl. May fail iff one or more |dispatchers| fails to serialize
......@@ -154,7 +155,8 @@ class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage {
// |thunks|. If the message is ever going to be routed to another node (see
// |WillBeRoutedExternally()| below), it will be serialized at that time using
// operations provided by |thunks|.
UserMessageImpl(ports::UserMessageEvent* message_event);
UserMessageImpl(ports::UserMessageEvent* message_event,
MojoCreateMessageFlags flags);
// Creates a serialized UserMessageImpl backed by an existing Channel::Message
// object. |header| and |user_payload| must be pointers into
......@@ -183,6 +185,10 @@ class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage {
// message.
Channel::MessagePtr channel_message_;
// Whether or not this message should enforce size constraints at
// serialization time.
const bool unlimited_size_ = false;
// Indicates whether any handles serialized within |channel_message_| have
// yet to be extracted.
bool has_serialized_handles_ = false;
......
......@@ -99,6 +99,12 @@ typedef uint32_t MojoCreateMessageFlags;
// No flags. Default behavior.
#define MOJO_CREATE_MESSAGE_FLAG_NONE ((uint32_t)0)
// Do not enforce size restrictions on this message, allowing its serialized
// payload to grow arbitrarily large. If this flag is NOT specified, Mojo will
// throw an assertion failure at serialization time when the message exceeds a
// globally configured maximum size.
#define MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE ((uint32_t)1)
// Options passed to |MojoCreateMessage()|.
struct MOJO_ALIGNAS(8) MojoCreateMessageOptions {
// The size of this structure, used for versioning.
......
......@@ -99,6 +99,7 @@ void CreateSerializedMessageObject(uint32_t name,
uint32_t trace_id,
size_t payload_size,
size_t payload_interface_id_count,
MojoCreateMessageFlags create_message_flags,
std::vector<ScopedHandle>* handles,
ScopedMessageHandle* out_handle,
internal::Buffer* out_buffer) {
......@@ -107,7 +108,7 @@ void CreateSerializedMessageObject(uint32_t name,
TRACE_EVENT_FLAG_FLOW_OUT);
ScopedMessageHandle handle;
MojoResult rv = mojo::CreateMessage(&handle);
MojoResult rv = CreateMessage(&handle, create_message_flags);
DCHECK_EQ(MOJO_RESULT_OK, rv);
DCHECK(handle.is_valid());
......@@ -194,9 +195,10 @@ void DestroyUnserializedContext(uintptr_t context) {
}
Message CreateUnserializedMessage(
std::unique_ptr<internal::UnserializedMessageContext> context) {
std::unique_ptr<internal::UnserializedMessageContext> context,
MojoCreateMessageFlags create_message_flags) {
ScopedMessageHandle handle;
MojoResult rv = mojo::CreateMessage(&handle);
MojoResult rv = CreateMessage(&handle, create_message_flags);
DCHECK_EQ(MOJO_RESULT_OK, rv);
DCHECK(handle.is_valid());
......@@ -230,24 +232,39 @@ Message::Message(Message&& other)
#endif
}
Message::Message(std::unique_ptr<internal::UnserializedMessageContext> context)
: Message(CreateUnserializedMessage(std::move(context))) {}
Message::Message(std::unique_ptr<internal::UnserializedMessageContext> context,
MojoCreateMessageFlags create_message_flags)
: Message(CreateUnserializedMessage(std::move(context),
create_message_flags)) {}
Message::Message(uint32_t name,
uint32_t flags,
size_t payload_size,
size_t payload_interface_id_count,
MojoCreateMessageFlags create_message_flags,
std::vector<ScopedHandle>* handles) {
CreateSerializedMessageObject(name, flags, GetTraceId(this), payload_size,
payload_interface_id_count, handles, &handle_,
&payload_buffer_);
CreateSerializedMessageObject(
name, flags, GetTraceId(this), payload_size, payload_interface_id_count,
create_message_flags, handles, &handle_, &payload_buffer_);
transferable_ = true;
serialized_ = true;
}
Message::Message(uint32_t name,
uint32_t flags,
size_t payload_size,
size_t payload_interface_id_count,
std::vector<ScopedHandle>* handles)
: Message(name,
flags,
payload_size,
payload_interface_id_count,
MOJO_CREATE_MESSAGE_FLAG_NONE,
handles) {}
Message::Message(base::span<const uint8_t> payload,
base::span<ScopedHandle> handles) {
MojoResult rv = mojo::CreateMessage(&handle_);
MojoResult rv = CreateMessage(&handle_, MOJO_CREATE_MESSAGE_FLAG_NONE);
DCHECK_EQ(MOJO_RESULT_OK, rv);
DCHECK(handle_.is_valid());
......
......@@ -51,8 +51,8 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message {
// Constructs a new message with an unserialized context attached. This
// message may be serialized later if necessary.
explicit Message(
std::unique_ptr<internal::UnserializedMessageContext> context);
Message(std::unique_ptr<internal::UnserializedMessageContext> context,
MojoCreateMessageFlags create_message_flags);
// Constructs a new serialized Message object with optional handles attached.
// This message is fully functional and may be exchanged for a
......@@ -64,6 +64,14 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) Message {
// Note that |payload_size| is only the initially known size of the message
// payload, if any. The payload can be expanded after construction using the
// interface returned by |payload_buffer()|.
Message(uint32_t name,
uint32_t flags,
size_t payload_size,
size_t payload_interface_id_count,
MojoCreateMessageFlags create_message_flags,
std::vector<ScopedHandle>* handles);
// Same as above, but the with default MojoCreateMessageFlags.
Message(uint32_t name,
uint32_t flags,
size_t payload_size,
......
......@@ -1193,6 +1193,16 @@ class LargeMessageTestImpl : public mojom::LargeMessageTest {
std::move(callback).Run(data.size());
}
void ProcessLotsOfData(const std::vector<uint8_t>& data,
ProcessLotsOfDataCallback callback) override {
std::move(callback).Run(data.size());
}
void GetLotsOfData(uint64_t data_size,
GetLotsOfDataCallback callback) override {
std::move(callback).Run(std::vector<uint8_t>(data_size));
}
private:
Receiver<mojom::LargeMessageTest> receiver_;
};
......@@ -1222,6 +1232,22 @@ TEST_P(RemoteTest, SendVeryLargeMessages) {
EXPECT_TRUE(did_dump_without_crashing);
}
did_dump_without_crashing = false;
data_size = 0;
ASSERT_TRUE(remote->ProcessLotsOfData(lots_of_data, &data_size));
EXPECT_EQ(kBigDataSize, data_size);
// Serialized or not, this message won't generate a crash report because it's
// explicitly marked with [UnlimitedSize].
EXPECT_FALSE(did_dump_without_crashing);
// [UnlimitedSize] also allows replies to be large.
did_dump_without_crashing = false;
lots_of_data.clear();
ASSERT_TRUE(remote->GetLotsOfData(kBigDataSize, &lots_of_data));
EXPECT_EQ(kBigDataSize, lots_of_data.size());
EXPECT_FALSE(did_dump_without_crashing);
base::debug::SetDumpWithoutCrashingFunction(nullptr);
}
......
......@@ -11,4 +11,10 @@ interface SharedRemoteSyncTest {
interface LargeMessageTest {
[Sync]
ProcessData(array<uint8> data) => (uint64 data_size);
[Sync, UnlimitedSize]
ProcessLotsOfData(array<uint8> data) => (uint64 data_size);
[Sync, UnlimitedSize]
GetLotsOfData(uint64 data_size) => (array<uint8> data);
};
......@@ -37,7 +37,7 @@ namespace {
Message CreateRawMessage(size_t size) {
ScopedMessageHandle handle;
MojoResult rv = CreateMessage(&handle);
MojoResult rv = CreateMessage(&handle, MOJO_CREATE_MESSAGE_FLAG_NONE);
DCHECK_EQ(MOJO_RESULT_OK, rv);
DCHECK(handle.is_valid());
......
......@@ -51,9 +51,13 @@ class MessageHandle {
using ScopedMessageHandle = ScopedHandleBase<MessageHandle>;
inline MojoResult CreateMessage(ScopedMessageHandle* handle) {
inline MojoResult CreateMessage(ScopedMessageHandle* handle,
MojoCreateMessageFlags flags) {
MojoCreateMessageOptions options = {};
options.struct_size = sizeof(options);
options.flags = flags;
MojoMessageHandle raw_handle;
MojoResult rv = MojoCreateMessage(nullptr, &raw_handle);
MojoResult rv = MojoCreateMessage(&options, &raw_handle);
if (rv != MOJO_RESULT_OK)
return rv;
......
......@@ -17,7 +17,7 @@ MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
size_t num_handles,
MojoWriteMessageFlags flags) {
ScopedMessageHandle message_handle;
MojoResult rv = CreateMessage(&message_handle);
MojoResult rv = CreateMessage(&message_handle, MOJO_CREATE_MESSAGE_FLAG_NONE);
DCHECK_EQ(MOJO_RESULT_OK, rv);
MojoAppendMessageDataOptions append_options;
......
......@@ -140,8 +140,8 @@ bool {{proxy_name}}::{{method.name}}(
{{interface_macros.build_message_flags(False, "kIsSync", "kExpectsResponse",
"kFlags")}}
{{interface_macros.build_serialized_message(
message_name, "param_%s", params_struct, params_description, "kFlags",
"message")}}
message_name, method, "param_%s", params_struct, params_description,
"kFlags", "message")}}
{%- endif %} {#- if method|method_supports_lazy_serialization #}
#if defined(ENABLE_IPC_FUZZER)
......@@ -196,8 +196,8 @@ void {{proxy_name}}::{{method.name}}(
{{interface_macros.build_message_flags(False, "kIsSync", "kExpectsResponse",
"kFlags")}}
{{interface_macros.build_serialized_message(
message_name, "in_%s", params_struct, params_description, "kFlags",
"message")}}
message_name, method, "in_%s", params_struct, params_description,
"kFlags", "message")}}
{%- endif %}
#if defined(ENABLE_IPC_FUZZER)
......@@ -352,7 +352,7 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
{%- else %}
{{interface_macros.build_message_flags(True, "is_sync_", "false", "kFlags")}}
{{interface_macros.build_serialized_message(
message_name, "in_%s", response_params_struct,
message_name, method, "in_%s", response_params_struct,
response_params_description, "kFlags", "message")}}
{%- endif %}
......
......@@ -97,11 +97,17 @@ base::OnceCallback<void(
{%- endif %}
{%- endmacro %}
{%- macro build_serialized_message(message_name, param_name_prefix,
{%- macro build_serialized_message(message_name, method, param_name_prefix,
params_struct, params_description,
flags_text, message_object_name) %}
{%- if method.unlimited_message_size %}
mojo::Message {{message_object_name}}(
{{message_name}}, {{flags_text}}, 0, 0,
MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE, nullptr);
{%- else %}
mojo::Message {{message_object_name}}(
{{message_name}}, {{flags_text}}, 0, 0, nullptr);
{%- endif %}
auto* buffer = {{message_object_name}}.payload_buffer();
{{params_struct|get_qualified_name_for_kind(internal=True)}}::BufferWriter
params;
......@@ -160,11 +166,16 @@ class {{message_typename}}
{%- for param in parameters %}
, std::move(param_{{param.name}})
{%- endfor %}
));
),
{%- if method.unlimited_message_size %}
MOJO_CREATE_MESSAGE_FLAG_UNLIMITED_SIZE);
{%- else %}
MOJO_CREATE_MESSAGE_FLAG_NONE);
{%- endif %}
}
DCHECK(serialize);
{{build_serialized_message(message_name, "param_%s", params_struct,
{{build_serialized_message(message_name, method, "param_%s", params_struct,
params_description, "kFlags", "message")}}
return message;
}
......
......@@ -86,7 +86,8 @@ class {{export_attribute}} {{struct.name}} {
static mojo::Message WrapAsMessage(UserType input) {
return mojo::Message(std::make_unique<
internal::{{struct.name}}_UnserializedMessageContext<
UserType, {{struct.name}}::DataView>>(0, 0, std::move(input)));
UserType, {{struct.name}}::DataView>>(0, 0, std::move(input)),
MOJO_CREATE_MESSAGE_FLAG_NONE);
}
template <typename UserType>
......
......@@ -256,6 +256,7 @@ ATTRIBUTE_MIN_VERSION = 'MinVersion'
ATTRIBUTE_EXTENSIBLE = 'Extensible'
ATTRIBUTE_STABLE = 'Stable'
ATTRIBUTE_SYNC = 'Sync'
ATTRIBUTE_UNLIMITED_SIZE = 'UnlimitedSize'
class NamedValue(object):
......@@ -976,6 +977,11 @@ class Method(object):
return self.attributes.get(ATTRIBUTE_SYNC) \
if self.attributes else None
@property
def unlimited_message_size(self):
return self.attributes.get(ATTRIBUTE_UNLIMITED_SIZE) \
if self.attributes else False
def __eq__(self, rhs):
return (isinstance(rhs, Method) and
(self.mojom_name, self.ordinal, self.parameters,
......
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