Post

Beyond Images: Bringing Rust Brotli to Edge Network Stack

Beyond Images: Bringing Rust Brotli to Edge Network Stack

Introduction

In our previous post we described how the Edge Security team turned the Rust PNG proof point into a repeatable rustification model and applied it to attacker-facing image formats such as BMP and ICO. We chose image codecs as our first target because they have clean dependency boundaries and are a potential attack vector, since they directly process attacker-controlled input. Codecs also tend to have good coverage via fuzzing and plenty of sample data to test against, which helps a lot with any rewrite or fix.

Our rustification goals were never limited to image codecs. Rustification is a strategy for every place in the browser where complex, untrusted, attacker-chosen bytes meet an unsafe implementation language. Image decoders are one such place. Compression codecs are another, and arguably a more consequential one, because they do not sit behind an <img> tag; they sit in the network stack, and together they parse every compressed response the browser receives.

This post is about our work replacing the C implementation of Brotli in Edge’s net component with the Rust brotli and brotli-decompressor crates (“Rust Brotli”), what the Edge experiment told us, how collaboration with the crates’ maintainer made it possible, and why this particular migration has an architectural payoff that image codecs do not.

Why Brotli, and why the network service

We evaluated several candidates for our next rustification effort. Deflate and Gzip were obvious options due to their widespread deployment across the web, but they are deeply embedded throughout Chromium’s network stack and ecosystem, making them significantly larger migrations with a broader blast radius. At the other end of the spectrum, Zstandard is a newer protocol with more limited browser deployment and lacked the same level of maturity and real-world browser validation in the Rust ecosystem at the time of our evaluation.

Brotli represented the most balanced opportunity. It is one of the most widely used content encodings on the modern web, giving the project immediate security impact, while also having a mature and actively maintained Rust implementation from the rust-brotli project, a crate with years of production usage, millions of downloads, and broad ecosystem adoption.

In Chromium, Brotli is used to decompress HTTP response bodies and TLS certificate chains delivered through certificate compression. Both code paths process bytes entirely controlled by the remote peer, and certificate decompression runs before the peer’s identity has even been verified. As a result, the Brotli decoder is often one of the first complex parsers that attacker-controlled data encounters when entering the browser, making it an ideal target for memory-safety improvements.

That combination is exactly what the Rule of 2 exists to address. The rule asks you to pick no more than two of: untrustworthy input, an unsafe implementation language, and high privilege. The network stack has untrustworthy input as a permanent, non-negotiable condition. Historically, Chromium bought down that risk by giving up the third factor, high privilege: it moved network handling into a separate, lower-privilege, sandboxed network service. That mitigation works, and it costs a process.

Rust changes the arithmetic. Chromium counts Rust as a safe implementation language, so a decoder written in Rust removes the unsafe language term from the equation entirely rather than compensating for it with isolation. In the short term, it is a straightforward severity reduction: a memory-corruption bug class disappears from the hot path. In the longer term, it opens a genuinely interesting architectural question: if the code parsing untrusted network bytes is memory-safe, is the process boundary still buying what it used to buy? We are not claiming we can collapse a process today. Brotli is one dependency among many in net, and the whole component would need to satisfy the same bar. But it is the direction the work points in, and that architectural payoff, which image codecs alone cannot offer, is why we think compression is worth the effort.

What the Edge experiment found

We did not want to argue this from first principles. Following the same “measure, don’t guess” approach we used for the image codecs, we shipped Rust Brotli under a feature flag and ran a controlled field trial in Edge, starting in Canary/Dev and progressing from there.

The experiment did two useful things:

First, the positive signal: performance held up, and overall crash rates did not regress in the way a pessimist would predict for a fresh implementation on the hottest parsing path in the browser. That gave us the data to keep funding the project and to approach Chromium with evidence rather than enthusiasm.

Second, and more valuable, it surfaced specific instabilities that neither our fuzzers nor the Rust Brotli test suites had produced. This is the part worth being explicit about, because “we found crashes” sounds like bad news, and in this case it is close to the opposite.

Every instability we found was a Rust panic, a controlled, deterministic abort at a known program point. Under Chromium’s build configuration, Rust is compiled with panic=abort, where catch_unwind is effectively a no-op, so a panic terminates the process cleanly instead of continuing into an undefined state. Because Rust makes these bugs reliability defects rather than security holes, the worst an attacker can achieve with them is a crash, not the memory corruption an equivalent C++ bug could lead to.

We treated them accordingly. Most of the panics came from assertions and unguarded arithmetic in paths that had likely never been exercised by the crates’ original consumers: server-side workloads where the same library both compresses and decompresses, and where malformed streams essentially never occur. A browser operates in a far more hostile environment: it decompresses whatever the network hands it, produced by encoders it has never seen. We rewrote those assertions into explicit error returns, hardened the bounds and overflow checks around the decompressor’s allocation and streaming paths, and contributed the fixes to Rust Brotli rather than carrying them as local patches in Edge. We also complemented the field data with a dedicated Rust Brotli stream fuzzer modelled on how net actually calls the decoder, and with scans from our AI-assisted vulnerability discovery tooling, which independently surfaced additional issues in both the rust-brotli and rust-brotli-decompressor repositories. Every one of those findings was reported to and fixed in Rust Brotli, including several that did not affect Edge at all.

Working with the maintainer

None of this would have landed within a meaningful timeframe without the collaboration of the maintainer of rust-brotli and rust-brotli-decompressor. Their responsiveness and willingness to engage on our fixes, reviews, and feature requests were a decisive factor in ensuring that the project evolved from a proof of concept into a solution suitable for deployment in a browser at scale.

Our initial outreach received feedback within hours, and review started the same day. The first wave of robustness improvements was merged quickly, followed by a second wave focused on reducing panic conditions, as required for the panic=abort environments used by Chromium and Edge. These contributions, tracked in our pull requests to both crates, helped bring both crates up to the reliability a browser requires.

The most time-critical piece was custom dictionary support. Full browser support for HTTP dictionary-compressed Brotli (dcb) requires the decompressor to attach an external dictionary to a stream, and that capability simply did not exist in Rust Brotli. It was the single missing feature blocking a complete browser-parity experiment. The maintainer took on that work directly and landed dictionary attachment support, and our team helped review it before it merged, combining manual review with scans from frontier AI models. All of this happened within the window we needed to keep the Edge experiment moving, and we have since updated our integration to the new crate version, which includes dictionary support.

What this changes

Brotli decompression in Edge can now run on a memory-safe implementation that has been validated in the field. Along the way, Rust Brotli became meaningfully more robust than when we started, for every consumer, not just us.

The strategic outcome is the most interesting one. Image codecs proved that memory-safe replacement works at the edges. Brotli is the first test of whether it also works in the core, on a dependency that is fundamental to the entire network stack, and that Chromium cannot treat casually. So far, the field results say it does.

Compression codecs are simply the next domino. The goal is that every parser standing between the open internet and your browser is written in a language that cannot be made to corrupt memory, and that when something does go wrong, the worst outcome is a crash we can fix, not a vulnerability someone else can sell.

Acknowledgements

We would like to thank Daniel Reiter Horn, maintainer of the rust-brotli and rust-brotli-decompressor crates, for the prompt reviews, collaboration, and support that helped accelerate the adoption and hardening of the crates.

This post is licensed under CC BY 4.0 by the author.