Commit 45dfa799 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Make the request and response validator stubs short tail calls with a lookup table.

This saves about 90 kB of Android APK size, and should be nearly as fast. This
adds up mainly because we have a sufficient number of Mojo methods that this
code is very heavily repeated.

For example, network::mojom::NetworkContextRequestValidator::Accept (1,592 bytes)
is replaced with a 24-byte function and a 720-byte table, and the strings
"NetworkContext RequestValidator" and "NetworkContext ResponseValidator" are
replaced with a single "NetworkContext" that may even be further deduped.

Change-Id: I772145a839b9607098311b3309fd49e94e43991e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1896773
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@google.com>
Auto-Submit: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713576}
parent 3f3c58c9
...@@ -153,6 +153,8 @@ component("bindings") { ...@@ -153,6 +153,8 @@ component("bindings") {
"lib/control_message_handler.h", "lib/control_message_handler.h",
"lib/control_message_proxy.cc", "lib/control_message_proxy.cc",
"lib/control_message_proxy.h", "lib/control_message_proxy.h",
"lib/generated_code_util.cc",
"lib/generated_code_util.h",
"lib/interface_endpoint_client.cc", "lib/interface_endpoint_client.cc",
"lib/interface_ptr_state.cc", "lib/interface_ptr_state.cc",
"lib/interface_ptr_state.h", "lib/interface_ptr_state.h",
......
// Copyright 2019 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 "mojo/public/cpp/bindings/lib/generated_code_util.h"
#include <cstring>
#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
namespace internal {
namespace {
GenericValidationInfo FindGenericValidationInfo(
uint32_t name,
base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
for (const auto& pair : info) {
if (pair.first == name)
return pair.second;
}
return {nullptr, nullptr};
}
GenericValidationInfo FindGenericValidationInfo(
uint32_t name,
base::span<const GenericValidationInfo> info) {
if (name >= info.size())
return {nullptr, nullptr};
return info[name];
}
template <typename T>
bool ValidateRequestGenericT(Message* message,
const char* class_name,
base::span<const T> info) {
if (!message->is_serialized() ||
ControlMessageHandler::IsControlMessage(message)) {
return true;
}
ValidationContext validation_context(message, class_name,
ValidationContext::kRequestValidator);
auto entry = FindGenericValidationInfo(message->header()->name, info);
if (!entry.request_validator) {
ReportValidationError(&validation_context,
VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
return false;
}
const bool message_is_request =
entry.response_validator ? ValidateMessageIsRequestExpectingResponse(
message, &validation_context)
: ValidateMessageIsRequestWithoutResponse(
message, &validation_context);
if (!message_is_request)
return false;
return entry.request_validator(message->payload(), &validation_context);
}
template <typename T>
bool ValidateResponseGenericT(Message* message,
const char* class_name,
base::span<const T> info) {
if (!message->is_serialized() ||
ControlMessageHandler::IsControlMessage(message)) {
return true;
}
ValidationContext validation_context(message, class_name,
ValidationContext::kResponseValidator);
if (!ValidateMessageIsResponse(message, &validation_context))
return false;
auto entry = FindGenericValidationInfo(message->header()->name, info);
if (!entry.response_validator) {
ReportValidationError(&validation_context,
VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
return false;
}
return entry.response_validator(message->payload(), &validation_context);
}
} // namespace
bool ValidateRequestGeneric(
Message* message,
const char* class_name,
base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
return ValidateRequestGenericT(message, class_name, info);
}
bool ValidateRequestGenericPacked(
Message* message,
const char* class_name,
base::span<const GenericValidationInfo> info) {
return ValidateRequestGenericT(message, class_name, info);
}
bool ValidateResponseGeneric(
Message* message,
const char* class_name,
base::span<const std::pair<uint32_t, GenericValidationInfo>> info) {
return ValidateResponseGenericT(message, class_name, info);
}
bool ValidateResponseGenericPacked(
Message* message,
const char* class_name,
base::span<const GenericValidationInfo> info) {
return ValidateResponseGenericT(message, class_name, info);
}
} // namespace internal
} // namespace mojo
// Copyright 2019 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 MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
#include <utility>
#include "base/component_export.h"
#include "base/containers/span.h"
namespace mojo {
class Message;
namespace internal {
class ValidationContext;
struct GenericValidationInfo {
// Non-null, unless this corresponds to a non-existent method.
bool (*request_validator)(const void* data, ValidationContext*);
// Non-null, unless this corresponds to a method that does not expect a
// response.
bool (*response_validator)(const void* data, ValidationContext*);
};
// Provides a generic implementation of the Mojo IPC request validation,
// allowing callers to do a compact tail call in generated code.
COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
bool ValidateRequestGeneric(
Message* message,
const char* class_name,
base::span<const std::pair<uint32_t, GenericValidationInfo>>);
// As above, but assumes that the ordinals (names) are packed such that a
// constant-time indexed table access is sufficient.
COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
bool ValidateRequestGenericPacked(Message* message,
const char* class_name,
base::span<const GenericValidationInfo>);
// Provides a generic implementation of the Mojo IPC response validation,
// allowing callers to do a compact tail call in generated code.
COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
bool ValidateResponseGeneric(
Message* message,
const char* class_name,
base::span<const std::pair<uint32_t, GenericValidationInfo>>);
// As above, but assumes that the ordinals (names) are packed such that a
// constant-time indexed table access is sufficient.
COMPONENT_EXPORT(MOJO_CPP_BINDINGS)
bool ValidateResponseGenericPacked(Message* message,
const char* class_name,
base::span<const GenericValidationInfo>);
} // namespace internal
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
...@@ -16,9 +16,11 @@ ValidationContext::ValidationContext(const void* data, ...@@ -16,9 +16,11 @@ ValidationContext::ValidationContext(const void* data,
size_t num_associated_endpoint_handles, size_t num_associated_endpoint_handles,
Message* message, Message* message,
const char* description, const char* description,
int stack_depth) int stack_depth,
ValidatorType validator_type)
: message_(message), : message_(message),
description_(description), description_(description),
validator_type_(validator_type),
data_begin_(reinterpret_cast<uintptr_t>(data)), data_begin_(reinterpret_cast<uintptr_t>(data)),
data_end_(data_begin_ + data_num_bytes), data_end_(data_begin_ + data_num_bytes),
handle_begin_(0), handle_begin_(0),
...@@ -44,16 +46,33 @@ ValidationContext::ValidationContext(const void* data, ...@@ -44,16 +46,33 @@ ValidationContext::ValidationContext(const void* data,
} }
} }
ValidationContext::ValidationContext(Message* message, const char* description) ValidationContext::ValidationContext(Message* message,
const char* description,
ValidatorType validator_type)
: ValidationContext(message->payload(), : ValidationContext(message->payload(),
message->payload_num_bytes(), message->payload_num_bytes(),
message->handles()->size(), message->handles()->size(),
message->payload_num_interface_ids(), message->payload_num_interface_ids(),
message, message,
description) {} description,
0,
validator_type) {}
ValidationContext::~ValidationContext() { ValidationContext::~ValidationContext() {
} }
std::string ValidationContext::GetFullDescription() const {
std::string full_description(description_);
switch (validator_type_) {
case kUnspecifiedValidator:
case kRequestValidator:
break;
case kResponseValidator:
full_description += " response";
break;
}
return full_description;
}
} // namespace internal } // namespace internal
} // namespace mojo } // namespace mojo
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/component_export.h" #include "base/component_export.h"
...@@ -25,6 +26,12 @@ namespace internal { ...@@ -25,6 +26,12 @@ namespace internal {
// indices in the payload of incoming messages. // indices in the payload of incoming messages.
class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext { class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext {
public: public:
enum ValidatorType {
kUnspecifiedValidator,
kRequestValidator,
kResponseValidator
};
// [data, data + data_num_bytes) specifies the initial valid memory range. // [data, data + data_num_bytes) specifies the initial valid memory range.
// [0, num_handles) specifies the initial valid range of handle indices. // [0, num_handles) specifies the initial valid range of handle indices.
// [0, num_associated_endpoint_handles) specifies the initial valid range of // [0, num_associated_endpoint_handles) specifies the initial valid range of
...@@ -40,11 +47,14 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext { ...@@ -40,11 +47,14 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext {
size_t num_associated_endpoint_handles, size_t num_associated_endpoint_handles,
Message* message = nullptr, Message* message = nullptr,
const char* description = "", const char* description = "",
int stack_depth = 0); int stack_depth = 0,
ValidatorType validator_type = kUnspecifiedValidator);
// As above, but infers most of the parameters from the Message payload. // As above, but infers most of the parameters from the Message payload.
// Used heavily in generated code and so affects binary size. // Used heavily in generated code and so affects binary size.
ValidationContext(Message* message, const char* description); ValidationContext(Message* message,
const char* description,
ValidatorType validator_type);
~ValidationContext(); ~ValidationContext();
...@@ -139,6 +149,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext { ...@@ -139,6 +149,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext {
Message* message() const { return message_; } Message* message() const { return message_; }
const char* description() const { return description_; } const char* description() const { return description_; }
std::string GetFullDescription() const;
private: private:
bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const { bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
...@@ -147,6 +158,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext { ...@@ -147,6 +158,7 @@ class COMPONENT_EXPORT(MOJO_CPP_BINDINGS_BASE) ValidationContext {
Message* const message_; Message* const message_;
const char* const description_; const char* const description_;
const ValidatorType validator_type_;
// [data_begin_, data_end_) is the valid memory range. // [data_begin_, data_end_) is the valid memory range.
uintptr_t data_begin_; uintptr_t data_begin_;
......
...@@ -77,17 +77,19 @@ void ReportValidationError(ValidationContext* context, ...@@ -77,17 +77,19 @@ void ReportValidationError(ValidationContext* context,
<< " (" << description << ")"; << " (" << description << ")";
} }
if (context->message()) { if (context->message()) {
context->message()->NotifyBadMessage(base::StringPrintf( context->message()->NotifyBadMessage(
"Validation failed for %s [%s (%s)]", context->description(), base::StringPrintf("Validation failed for %s [%s (%s)]",
ValidationErrorToString(error), description)); context->GetFullDescription().c_str(),
ValidationErrorToString(error), description));
} }
} else { } else {
if (!g_suppress_logging) if (!g_suppress_logging)
LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
if (context->message()) { if (context->message()) {
context->message()->NotifyBadMessage(base::StringPrintf( context->message()->NotifyBadMessage(
"Validation failed for %s [%s]", context->description(), base::StringPrintf("Validation failed for %s [%s]",
ValidationErrorToString(error))); context->GetFullDescription().c_str(),
ValidationErrorToString(error)));
} }
} }
} }
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
{%- set class_name = interface.name %} {%- set class_name = interface.name %}
{%- set proxy_name = interface.name ~ "Proxy" %} {%- set proxy_name = interface.name ~ "Proxy" %}
{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} {%- set namespace_as_string = "%s"|format(module_namespace|replace(".","::")) %}
{%- set namespace_as_string_with_variant = namespace_as_string ~ ("::" ~ variant if variant) %}
{%- set qualified_class_name = ("::" ~ namespace_as_string_with_variant if namespace_as_string_with_variant) ~ "::" ~ class_name %}
{%- macro alloc_params(struct, params, message, method_number, is_response) %} {%- macro alloc_params(struct, params, message, method_number, is_response) %}
mojo::internal::SerializationContext serialization_context; mojo::internal::SerializationContext serialization_context;
...@@ -518,82 +520,58 @@ bool {{class_name}}StubDispatch::AcceptWithResponder( ...@@ -518,82 +520,58 @@ bool {{class_name}}StubDispatch::AcceptWithResponder(
return false; return false;
} }
{% if interface.methods and (interface | has_packed_method_ordinals) %}
static const mojo::internal::GenericValidationInfo k{{class_name}}ValidationInfo[] = {
{%- for i in range(interface.methods | map(attribute='ordinal') | max + 1) -%}
{%- set method = (interface.methods | selectattr('ordinal', 'equalto', i) | list)[0] %}
{%- if method %}
{&internal::{{class_name}}_{{method.name}}_Params_Data::Validate,
{%- if method.response_parameters != None %}
&internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate},
{%- else %}
nullptr /* no response */},
{%- endif %}
{%- else %}
{nullptr, nullptr}, // nonexistent
{%- endif %}
{%- endfor %}
};
{%- elif interface.methods %}
static const std::pair<uint32_t, mojo::internal::GenericValidationInfo> k{{class_name}}ValidationInfo[] = {
{%- for method in interface.methods %}
{internal::k{{class_name}}_{{method.name}}_Name,
{&internal::{{class_name}}_{{method.name}}_Params_Data::Validate,
{%- if method.response_parameters != None %}
&internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate}},
{%- else %}
nullptr /* no response */}},
{%- endif %}
{%- endfor %}
};
{%- endif %}
{#--- Request validator definitions #} {#--- Request validator definitions #}
bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
if (!message->is_serialized() || {#- Not simply Name_ because there is a mojom interface called MessageReceiver #}
mojo::internal::ControlMessageHandler::IsControlMessage(message)) { const char* name = {{qualified_class_name}}::Name_;
return true; {%- if not interface.methods %}
} return mojo::internal::ValidateRequestGeneric(message, name, {});
{%- elif interface | has_packed_method_ordinals %}
mojo::internal::ValidationContext validation_context( return mojo::internal::ValidateRequestGenericPacked(message, name, k{{class_name}}ValidationInfo);
message, "{{class_name}} RequestValidator"); {%- else %}
return mojo::internal::ValidateRequestGeneric(message, name, k{{class_name}}ValidationInfo);
switch (message->header()->name) { {%- endif %}
{%- for method in interface.methods %}
case internal::k{{class_name}}_{{method.name}}_Name: {
{%- if method.response_parameters != None %}
if (!mojo::internal::ValidateMessageIsRequestExpectingResponse(
message, &validation_context)) {
return false;
}
{%- else %}
if (!mojo::internal::ValidateMessageIsRequestWithoutResponse(
message, &validation_context)) {
return false;
}
{%- endif %}
if (!mojo::internal::ValidateMessagePayload<
internal::{{class_name}}_{{method.name}}_Params_Data>(
message, &validation_context)) {
return false;
}
return true;
}
{%- endfor %}
default:
break;
}
// Unrecognized message.
ReportValidationError(
&validation_context,
mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
return false;
} }
{#--- Response validator definitions #} {#--- Response validator definitions #}
{% if interface|has_callbacks %} {% if interface|has_callbacks %}
bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
if (!message->is_serialized() || const char* name = {{qualified_class_name}}::Name_;
mojo::internal::ControlMessageHandler::IsControlMessage(message)) { {%- if interface | has_packed_method_ordinals %}
return true; return mojo::internal::ValidateResponseGenericPacked(message, name, k{{class_name}}ValidationInfo);
} {%- else %}
return mojo::internal::ValidateResponseGeneric(message, name, k{{class_name}}ValidationInfo);
mojo::internal::ValidationContext validation_context( {% endif %}
message, "{{class_name}} ResponseValidator");
if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
return false;
switch (message->header()->name) {
{%- for method in interface.methods if method.response_parameters != None %}
case internal::k{{class_name}}_{{method.name}}_Name: {
if (!mojo::internal::ValidateMessagePayload<
internal::{{class_name}}_{{method.name}}_ResponseParams_Data>(
message, &validation_context)) {
return false;
}
return true;
}
{%- endfor %}
default:
break;
}
// Unrecognized message.
ReportValidationError(
&validation_context,
mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD);
return false;
} }
{%- endif -%} {%- endif -%}
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/common/task_annotator.h" #include "base/task/common/task_annotator.h"
#include "mojo/public/cpp/bindings/lib/generated_code_util.h"
#include "mojo/public/cpp/bindings/lib/message_internal.h" #include "mojo/public/cpp/bindings/lib/message_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h" #include "mojo/public/cpp/bindings/lib/serialization_util.h"
#include "mojo/public/cpp/bindings/lib/unserialized_message_context.h" #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h"
......
...@@ -177,6 +177,13 @@ def ShouldInlineUnion(union): ...@@ -177,6 +177,13 @@ def ShouldInlineUnion(union):
for field in union.fields) for field in union.fields)
def HasPackedMethodOrdinals(interface):
"""Returns whether all method ordinals are packed such that indexing into a
table would be efficient."""
max_ordinal = len(interface.methods) * 2
return all(method.ordinal < max_ordinal for method in interface.methods)
class StructConstructor(object): class StructConstructor(object):
"""Represents a constructor for a generated struct. """Represents a constructor for a generated struct.
...@@ -367,6 +374,7 @@ class Generator(generator.Generator): ...@@ -367,6 +374,7 @@ class Generator(generator.Generator):
"get_pad": pack.GetPad, "get_pad": pack.GetPad,
"get_qualified_name_for_kind": self._GetQualifiedNameForKind, "get_qualified_name_for_kind": self._GetQualifiedNameForKind,
"has_callbacks": mojom.HasCallbacks, "has_callbacks": mojom.HasCallbacks,
"has_packed_method_ordinals": HasPackedMethodOrdinals,
"has_sync_methods": mojom.HasSyncMethods, "has_sync_methods": mojom.HasSyncMethods,
"method_supports_lazy_serialization": "method_supports_lazy_serialization":
self._MethodSupportsLazySerialization, self._MethodSupportsLazySerialization,
......
...@@ -940,6 +940,12 @@ template("mojom") { ...@@ -940,6 +940,12 @@ template("mojom") {
if (enable_kythe_annotations) { if (enable_kythe_annotations) {
args += [ "--enable_kythe_annotations" ] args += [ "--enable_kythe_annotations" ]
} }
if (!defined(invoker.scramble_message_ids) ||
invoker.scramble_message_ids) {
inputs += message_scrambling_inputs
args += message_scrambling_args
}
} }
} }
......
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