CVE-2026-111497 in the Brotli decoder, and why mbrotli is not affected

CVE-2026-111497 is an out-of-bounds write in Google Brotli's C decoder, versions 1.0.3 to 1.2.0. What the fix in Brotli 1.2.1 changes, who has to upgrade, and why the safe-Rust decoder of mbrotli and the ngx_mbrotli module are not affected.

Summary

  • CVE-2026-111497 is an out-of-bounds write (CWE-787) in the C decoder of Google Brotli, c/dec/decode.c, in versions 1.0.3 through 1.2.0. A crafted Brotli stream can corrupt the decoder’s own state. Google rates it 9.2 Critical under CVSS 4.0.
  • The fix is Brotli 1.0.10, 1.1.1 or 1.2.1, released on 21 October 2026. Anything that decodes untrusted Brotli with the C library has to upgrade: language runtimes, HTTP clients, proxies, browsers and the tools that link it.
  • The root cause is an unchecked return value (CWE-252): on the decoder’s fast path, a failed block switch was ignored and decoding went on. Brotli 1.2.1 turns that failure into the error BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH.
  • mbrotli’s decoder is written in Rust with unsafe forbidden, so a stream cannot make it write outside its buffers. Before 0.5.17 it accepted the same RFC-invalid streams without corrupting anything; since 0.5.17 it rejects them like Brotli 1.2.1.
  • ngx_mbrotli only compresses. It is built without mbrotli’s decoder, so the CVE has no path into it.

On 22 October 2026 Google published CVE-2026-111497, an out-of-bounds write in the decoder of Google Brotli, the C library behind most Brotli decompression in browsers, runtimes and servers. Brotli 1.0.10, 1.1.1 and 1.2.1, released a day earlier, fix it.

This post covers what the bug is, who needs to upgrade, and why mbrotli, our Brotli codec in Rust, and ngx_mbrotli, our NGINX module, are not affected.

What the CVE says

   
ID CVE-2026-111497
Product Google Brotli, decoder (c/dec/decode.c)
Affected 1.0.3 to 1.0.9, 1.1.0, 1.2.0
Fixed 1.0.10, 1.1.1, 1.2.1 (21 October 2026)
Weakness CWE-787 Out-of-bounds Write, CWE-252 Unchecked Return Value
Severity CVSS 4.0 base score 9.2, Critical
Vector AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H
Found by OpenAI Codex Security: Khai Tran and Sylvie Mayer

In the words of the record, a crafted Brotli stream lets “a remote unauthenticated attacker to corrupt adjacent decoder state memory, disclose sensitive memory contents, cause a denial of service, or potentially execute arbitrary code”. The attacker only has to get a Brotli body in front of a vulnerable decoder: an HTTP response with Content-Encoding: br, an uploaded .br file, a compressed message on a queue. Node.js shipped security releases for it in October 2026.

Who has to upgrade

Anything that decodes untrusted Brotli with the C library:

  • language runtimes and their zlib/brotli bindings, such as Node.js;
  • HTTP clients and proxies that decompress br responses or request bodies;
  • tools and libraries that bundle the C decoder, including statically linked binaries, which a distribution update of libbrotli does not reach.

The CVE is in the decoder only. Brotli 1.2.1 changes c/dec/decode.c and the decoder header and nothing in the encoder.

What the fix changes

The fix in all three releases is one upstream commit, Detect and report invalid stream earlier (0ed4268, committed on 27 February 2026 and released in October).

A Brotli meta-block splits its literals, commands and distances into blocks. Each of the three categories has one or more block types, and a block switch reads the next type and length from the stream. A category with a single type has nothing to switch to: RFC 7932 gives it a block count of 16,777,216, and a stream that needs a switch after that count runs out is invalid.

In Brotli 1.2.0 the function that decodes a block switch returned a boolean, and it returned false in exactly that case. The decoder’s slow path read that false as “needs more input”. Its fast path did not read it at all: the three Decode*BlockSwitch wrappers returned void. The decoder went on with state the switch never set up, and the CVE record’s CWE-252, Unchecked Return Value, names that ignored result.

Brotli 1.2.1 makes the switch return a BrotliDecoderErrorCode, passes the result up through a new BROTLI_SAFE_WITH_STATUS macro on both paths, and reports BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH when a single-type category runs out. The invalid stream now fails at the switch.

Why mbrotli is not affected

mbrotli does not link the C library. Its decoder is a separate implementation of RFC 7932 in Rust, and the crate is built with

#![cfg_attr(not(test), forbid(unsafe_code))]

so the crate’s own code has no unsafe block. The ring buffer, the Huffman tables and the decoder state are ordinary slices and structs, and safe Rust checks every index into them. A logic error of the same kind as the one fixed in Brotli 1.2.1 can make the Rust decoder accept a stream it should reject, return an error, or at worst panic. It cannot write past the end of a buffer into neighbouring state, which is the step that turns a format bug into the memory corruption this CVE describes.

The C decoder in brotli-ffi that mbrotli’s repository vendors is a dev-dependency. The tests use it as the reference to check mbrotli’s output against; it is never built into the published crate.

What mbrotli did with these streams

We checked the CVE’s streams against mbrotli’s decoder rather than stopping at “it’s Rust”. mbrotli 0.5.16 and earlier started the block count of a single-type category at u64::MAX, not at the RFC’s 16,777,216. That count never ran out in practice, so these streams decoded without an error. Nothing was corrupted, but mbrotli accepted streams that the RFC calls invalid and that Brotli 1.2.1 now rejects.

mbrotli 0.5.17, released on 24 October 2026, starts the count at 16,777,216 and returns InvalidDataKind::MetaBlock when a single-type category needs an element after the count runs out. The single-type case also skips the fast block switch, so the hot loop pays no extra check. Two new tests decode a stream built to sit on each side of that boundary and compare with the patched C decoder:

  • 224 − 1 commands in a single-type block: both decoders accept it and produce the same bytes;
  • 224 commands: C returns BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH and mbrotli returns InvalidDataKind::MetaBlock. mbrotli is checked in one call and again with the tail fed one byte at a time through the streaming API, which takes the slow path.

Upgrade to 0.5.17 if you want mbrotli and Brotli 1.2.1 to agree on which streams are valid, for example when one component checks an input with the C decoder and another decodes it with mbrotli. It is not a memory-safety fix.

Why ngx_mbrotli is not affected

ngx_mbrotli is an NGINX filter that compresses responses on the fly. It never decodes Brotli, and its build reflects that: it depends on mbrotli with default-features = false and only the compression feature, so mbrotli’s decoder is not compiled into the module at all. The decoder is enabled only in its tests, which decode what the filter produced.

NGINX itself does not decode Brotli either. If a proxy in front of or behind it does, for example to inspect or rewrite br bodies, that proxy needs Brotli 1.2.1 or one of the other fixed releases.

What to do

  1. Find every copy of the C Brotli decoder you run: system libbrotli, bundled copies in runtimes and language packages, statically linked binaries.
  2. Upgrade to Brotli 1.0.10, 1.1.1 or 1.2.1, or to a release of the runtime that bundles one of them.
  3. If you use mbrotli, nothing is urgent. Move to 0.5.17 when convenient to get the same rejection of invalid streams as Brotli 1.2.1.
  4. ngx_mbrotli needs no change.

Security reports for mbrotli and the other M-Compressors projects go through the private process in each repository’s SECURITY.md.

Questions

What is CVE-2026-111497?

An out-of-bounds write in the Brotli decoder of Google Brotli 1.0.3 through 1.2.0. A crafted compressed stream can corrupt adjacent decoder state, disclose memory, crash the process or potentially execute code. It is fixed in Brotli 1.0.10, 1.1.1 and 1.2.1.

Which Brotli versions are affected by CVE-2026-111497?

1.0.3 up to but not including 1.0.10, 1.1.0, and 1.2.0. Upgrade to 1.0.10, 1.1.1 or 1.2.1, whichever line you are on.

Is the Brotli encoder affected?

No. The CVE record names the decoder, c/dec/decode.c, and the fix changes only the decoder. Software that only compresses with Brotli is not exposed through this bug, though it should still be upgraded if it ships the decoder too.

Is mbrotli affected by CVE-2026-111497?

No. mbrotli does not use the C library; its decoder is a separate implementation in Rust, and the crate forbids unsafe code. A malformed stream can end in an error, not in a write outside a buffer. Versions before 0.5.17 accepted the RFC-invalid streams behind this CVE; 0.5.17 rejects them with InvalidDataKind::MetaBlock, matching Brotli 1.2.1.

Is ngx_mbrotli affected by CVE-2026-111497?

No. ngx_mbrotli compresses responses and never decodes Brotli. It depends on mbrotli with only the compression feature, so no decoder is compiled into the module.

Do I need to upgrade mbrotli?

Not for memory safety. Upgrade to 0.5.17 if you want mbrotli to reject the same invalid streams as Brotli 1.2.1, for example when another component checks the same input with the C decoder.

Mikhail Panfilov

Backend developer from Saint Petersburg, working at Aisory. Author of mbrotli, mzstd and the ngx_mbrotli and ngx_mzstd NGINX modules: Brotli and Zstandard rewritten in safe Rust.

Cite this post

Mikhail Panfilov. “CVE-2026-111497 in the Brotli decoder, and why mbrotli is not affected.” M-Compressors, 24 October 2026. https://m-compressors.github.io/2026/brotli-cve-2026-111497-mbrotli/