For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎

Message Validation & Error Handling

All interface messages are validated upon receipt before being dispatched to the receiver. This helps to ensure consistent validation across interfaces without leaving the burden to developers and security reviewers every time a new message is added. It is safe to assume that any incoming method call (or response callback) has already had its parameters validated.

Built-in Validation Behavior

If a message fails validation, it is never dispatched. Instead a connection error is triggered (see Error Handling below.)

Some baseline level of validation is done automatically for primitive mojom types.

Non-Nullable Objects

Mojom field or parameter values (e.g. structs, interfaces, arrays, etc.) may be marked nullable in mojom definitions (TODO: ref mojom doc). If a field or parameter is not marked nullable but a message is received with a null value in its place, that message will fail validation.

Enums

Enums declared in mojom are automatically validated against the range of possible values. For example, if a mojom file declares the enum:

enum AdvancedBoolean {
  TRUE = 0,
  FALSE = 1,
  FILE_NOT_FOUND = 2,
}

and a message is received with a value of 3 (or anything other than 0, 1, or 2) in place of some AdvancedBoolean-typed field, the message will fail validation.

NOTE: It's possible to avoid this behavior by marking the enum with the [Extensible] attribute in its mojom definition, but this should be avoided unless you antipicate your messages going between two different versions of Chrome at runtime. (Read more about versioning here.)This is generally not something we have to be concerned about in normal Chrome installations.

Other Failures

There are a host of internal validation errors that may occur when malformed messages are received, but developers should not be concerned with these specifically; in general they can only result from internal Mojo bugs or compromised processes which have been tampered with. In the event that any such validation errors do occur, a connection error is triggered the same as in the above cases.

Custom Validation

It's also possible for developers to define custom validation logic for specific mojom struct types. This is supported implicitly by using Type Mapping. Validation failures triggered by this mechanism result in the same kind of connection errors triggered above.

Error Handling

All message pipe-binding C++ objects (e.g., mojo::Binding, mojo::StrongBinding, mojo::InterfacePtr) support setting a connection error handler via the aptly named set_connection_error_handler method.

An error handler is simply a closure invoked any time an unrecoverable error occurs on a message pipe. This can occur if the remote end of the pipe is closed manually or automatically (due to a validation error, for example), or if its process crashed, etc.

A connection error will only be signaled at most once on any given binding object, and once a connection error is signaled, that object is no longer in a working state. It is safe to reuse (i.e. rebind) the object, and for InterfacePtrs it is even safe to continue making calls on it, but messages are no longer flowing in either direction and such calls are effectively no-ops.

It's important to note that the error handler will never be invoked once its corresponding binding object has been destroyed. Consider the following code:

class Foo : public mojom::Foo {
 public:
  Foo(mojom::FooRequest request) : binding_(this, std::move(request)) {
    binding_.set_connection_error_handler(
        base::Bind(&Foo::OnError, base::Unretained(this));
  }

  // ...

 private:
  void OnError() { /* ... */ }

  mojo::Binding<mojom::Foo> binding_;
};

In the example above, the use of base::Unretained is perfectly safe because this owns binding_ and the error handler will not be invoked once binding_ (and thus once this) is destroyed.

The same is true for InterfacePtrs, so a Foo client could do something similiar:

class FooClient {
 public:
  // ...

  void MakeRequest() {
    foo_->DoSomething(
        base::Bind(&FooClient::OnResponse, base::Unretained(this)));
  }

 private:
  void OnResponse() { /* ... */ }

  mojom::FooPtr foo_;  // Remember, mojom::FooPtr is an alias for mojo::InterfacePtr<mojom::Foo>
};

Once again, use of base::Unretained is safe because the lifetime of the callback is bounded by the lifetime of the FooPtr.
Comments