# Decoder writes past undersized buffer

Decoder output length not checked against destination capacity; buffer sized from a different, smaller number

_Area: Memory corruption and native code. Seen in 3 disclosed reports; latest 2026-10-22. Updated 2026-11-08._

## How it works
A decoder produces output whose length the attacker influences, but the destination buffer is sized from a different, smaller number: a fixed constant, a configured block size, or a budget that ignores one segment. The write trusts the produced length instead of comparing it to space left. The broken assumption is "output length is bounded by what we allocated" — nothing enforces it, so bytes land past the end in adjacent heap. A close cousin on the read side: a read length and a buffer size derived from different fields, so the read overruns the buffer [E3](https://bugbountylibrary.com/r/h1%3A550696.md).

## Where it shows up
- Crypto/decrypt paths where one algorithm validates decrypted length and a sibling path does not [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md).
- Fixed-size stack or heap buffers (e.g. 1024 bytes) fed by attacker-supplied encoded input [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md).
- Resumable encoder/decoder state machines wrapping a library destination manager: some states guard free space, one does not [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md).
- Buffer sizing that accounts for EXIF/ICC/metadata but reserves nothing for a later segment (comment, trailer) [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md).
- Read-side: a CLI/option value that overrides a default, where the read length and the buffer size come from different fields [E3](https://bugbountylibrary.com/r/h1%3A550696.md).

## How to review
For every write into a buffer, find where the buffer size comes from and where the write length comes from; they must be the same source, or the write must be bounded by remaining space. Check the exact invariant: bytes_written + produced <= capacity, tested before each write, not after. In state machines, enumerate every state that emits and confirm each has the guard [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md). In crypto code, diff the validation between algorithm paths [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md). For option overrides, grep every downstream use of the struct field to confirm it was updated [E3](https://bugbountylibrary.com/r/h1%3A550696.md). Treat "exact fit" as unsafe when the library writes before testing its counter [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md).

## How finders spotted it
- Noticed incorrect buffer-size math around OpenSSL calls, focused on OpenSSL usage, and found AESDecode lacked the length check its RSA sibling had [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md).
- Code-read the resumable encoder: every emitting state (2, 5, 6) guarded free_in_buffer but state 4 did not; traced _save() sizing and reasoned about libjpeg's post-decrement emit semantics to find the exact-fit wrap [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md).
- Read lib/tftp.c and saw state->blksize was never updated from --tftp-blksize, so recv length and buffer size diverged [E3](https://bugbountylibrary.com/r/h1%3A550696.md).

## Variants seen
- Missing length check in one crypto path vs another [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md).
- Missing free-space guard in one encoder state, plus exact-fit counter wrap [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md).
- Read-side: stale default field used as read length while buffer sized from user option [E3](https://bugbountylibrary.com/r/h1%3A550696.md).

## Impact and severity
Heap overflow leading to RCE: zero-click in Zoom chat [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md). Severity rises when chained with an info leak to defeat ASLR [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md), with non-CFG libraries to bypass Control Flow Guard [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md), or with heap grooming to gain control of a function pointer [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md). Reachability scales impact: the same decoder is exposed by other network-facing software beyond the original target [E1](https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **Pillow (pip)** >= 9.4.0, < 13.0.0, fixed in 13.0.0: Pillow: Heap buffer overflow writing JPEG comment after ICC profile fills buffer (CVE-2026-96778). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md. Fix: https://github.com/python-pillow/Pillow/commit/02561b8dc6117d5670d4b469fecdd02c70854f9b
- **curl**: CVE-2019-5436: Heap Buffer Overflow at lib/tftp.c (CVE-2019-5436). Report: https://bugbountylibrary.com/r/h1%3A550696.md

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Asector7-computest-nl-post-2021-08-zoom.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-g7cr-3p24-7wf7.md
- E3: https://bugbountylibrary.com/r/h1%3A550696.md
