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
unsafeforbidden, 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/brotlibindings, such as Node.js; - HTTP clients and proxies that decompress
brresponses or request bodies; - tools and libraries that bundle the C decoder, including statically linked
binaries, which a distribution update of
libbrotlidoes 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_SWITCHand mbrotli returnsInvalidDataKind::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
- Find every copy of the C Brotli decoder you run: system
libbrotli, bundled copies in runtimes and language packages, statically linked binaries. - 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.
- 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.
- 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.
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/