Skip to content

Safer IPC Guidelines

Simon Lewis edited this page Mar 2, 2026 · 3 revisions

WebKit Guidelines for Safer IPC Programming

See also guidelines for Safer C++.

The purpose of these guidelines is to ensure the robustness and integrity of an application using WebKit assuming the presence of a powerful attacker. The modern WebKit architecture is multi-process with IPC security forming a critical security boundary.

Threat Model

The threat model assumes that WebContent is fully compromised at all times and that we must never directly trust any data it provides.

Guidelines

The following guidelines if followed will reduce the majority of risk commonly seen with IPC changes.

CoreIPC Receivers must use DispatchedFrom/To and EnabledBy

In the CoreIPC messages.in IDL files there are three annotations which enable us to know if code is in the WebContent accessible attack surface

  • DispatchedFrom: Where a CoreIPC message can be sent from.
    • Messages and replies sent directly from WebContent to privileged processes are a high priority in terms of security risk.
  • DispatchedTo: Which process is receiving the CoreIPC message.
  • EnabledBy: The setting which enables a subsystem to receive messages.
    • Features turned off by default are less of a security concern.

Developers must ensure that new IPC message receivers have these three annotations to pick up relevant RELEASE_ASSERT security checks.

Correct EnabledBy usage

messages -> GPUConnectionToWebProcess {
  [EnabledBy=WebGPUEnabled] void CreateRemoteGPU(WebKit::WebGPUIdentifier identifier)
  [EnabledBy=WebGPUEnabled] void ReleaseRemoteGPU(WebKit::WebGPUIdentifier identifier)
}

Incorrect EnabledBy omission:

messages -> GPUConnectionToWebProcess {
  // These messages are only sent when the WebGPU feature is turned on.
  void CreateRemoteGPU(WebKit::WebGPUIdentifier identifier)
  void ReleaseRemoteGPU(WebKit::WebGPUIdentifier identifier)
}

Messages parameters must not contain opaque data and must be validated in privileged processes

The handling of IPC data is inherently risky and memory safety is used to reduce the risk of a security problem: The SaferCpp and Strictly Safe Swift efforts contribute to reduce the risk in addition to IPC level message validation in the IDL files (Validator=“<expression>”) and MESSAGE_CHECK assertions in the receiving functions.

Opaque data is a collection of bytes (e.g. Vector<uint8_t>) where the typing information gives no hint as to the underlying structure of the data. Parsing of unstructured data is a security risk and that must happen only in the WebContent Process (in strong containment).

Complex data which is sent from WebContent Process to privileged processes must be specifically typed and broken down to primitive types in the serialization.in IDL files. By breaking down the types to primitives we ensure that the risky activity happens in strong containment and gives the privileged processes the best chance of avoiding a memory safety issue when reconstructing the objects during deserialization in the privileged processes.

An example of a bad pattern is image data flowing from WebContent Process into an image parsing function in a privileged process. For complex image data (e.g. data for example which might be PNG, JPEG, or SVG in its original data format) SharedBitmap is the only approved safe way to transfer images from WebContent Process to higher privileged processes.

WebKit developers must ensure that the types they send from WebContent Process are considered primitive (from a security point of view, see LayoutTests/ipc/serialized-type-info.html).

WebKit Developers must use existing primitive types where possible. to ensure the goal of: ‘a small and well tested set of objects’ being used in IPC data transfer.

WebKit Developers must not tunnel complex data through types like String using base64, JSON or similar.

WebContent originated data flows must not configure privileged processes behavior

WebContent Process is untrusted and privileged processes (UI, GPU, Networking) must not make security decisions based on the data sent from it.

An example of a WebKit bug with security implications is WebGL context primitive restart can be toggled from WebContent process. The patch addresses a problem where WebContent Process could configure the GPU Process features available over IPC further exposing unexpected functionality to the untrusted WebContent processes.

Developers must consider carefully the design of IPC message flows and ensure debugging interfaces are disabled in Release builds.

Always validate data coming from IPC

Reasoning:

A child process can become compromised and we should therefore not trust any data it sends other processes via IPC. Failure to properly validate received data can lead to sandbox escapes or denial of service attacks (by crashing the recipient process). In general, IPC from WebContent processes have the highest risk. However, other child processes can in theory get compromised too and send bad IPC.

It is never acceptable for bad IPC from a compromised process to cause the recipient to crash, even in a non-exploitable way (such as a RELEASE_ASSERT()). Also, instead of ignoring bad IPC, it is better practice to kill the compromised process so it cannot keep trying to find an exploit.

Validation can happen at two levels:

  • Ideally during IPC deserialization, in generated code (using [Validator=X] in *.serialization.in files)
  • Otherwise, after deserialization using a MESSAGE_CHECK()

Both will cause the termination of the sender.

Right:

class WebCore::ShareableBitmapConfiguration {
    [Validator='m_size->width() >= 0 && m_size->height() >= 0'] WebCore::IntSize m_size;
};

Wrong:

class WebCore::ShareableBitmapConfiguration {
    WebCore::IntSize m_size; // Then assume m_size contains positive values.
};

Right:

void Foo::setCookiesFromDOM(const URL& firstParty, const String& cookies)
{
    MESSAGE_CHECK(allowsFirstPartyForCookies(m_processIdentifier, firstParty));
    // Set cookies...
}

Wrong:

void Foo::setCookiesFromDOM(const URL& firstParty, const String& cookies)
{
    // Assume this WebContent process is allowed to set cookies for this
    // |firstParty| and set cookies...
}

Manual encode() / decode() functions should no longer be used for IPC serialization

Reasoning:

Manually writing encode() or decode() for a class / struct is no longer acceptable in new code. Most of our IPC serializers / deserializers get generated from an IDL format in *.serialization.in files (e.g. WebCoreArgumentCoders.serialization.in), and any remaining classes with manual encode() / decode() methods will be transitioned soon.

Generating serialization methods reduces the risk of bugs, automatically includes validation logic when encoding and decoding, and supports automatic testing of serialized types.

Right:

// In WebCoreArgumentCoders.in:
struct WebCore::FloatPoint {
    float m_x;
    float m_y;
}

Wrong:

// In FloatPoint.h:
namespace WebCore {
struct FloatPoint {
    float m_x;
    float m_y;

    template<typename Encoder>
    void encode(Encoder& encoder) const
    {
        encoder << m_x << m_y;
    }

    template<typename Decoder>
    static std::optional<FloatPoint> decode(Decoder& decoder)
    {
        std::optional<float> x;
        decoder >> x;
        if (!x)
            return std::nullopt;
        std::optional<float> y;
        decoder >> y;
        if (!y)
            return std::nullopt;
        return FloatPoint { *x, *y };
    }
}
} // namespace WebCore

Clone this wiki locally