#340·qoi

qoi_decode accepts truncated/no-chunk QOI streams and can allocate large output buffers from tiny files

Author: damselengCreated Jun 5, 2026Updated Jun 6, 2026

Summary

I found that qoi_decode() / qoi_read() can accept malformed QOI streams that do not contain enough image data to produce the declared number of pixels.

In these cases, the decoder returns success instead of rejecting the input as invalid. This can also allow very small .qoi files to trigger allocation of a much larger output buffer based only on the declared dimensions.

I am reporting this as a decoder correctness and resource-amplification hardening issue, not as memory corruption. I did not observe an ASan/libFuzzer memory-corruption crash.

Affected version tested

I tested the current master branch:

commit: 97bacc86a9c4abf5a2d452102dc26546c4c670b9

I did not find a direct phoboslab/qoi OSS-Fuzz project in my current google/oss-fuzz/projects checkout.

Observed behavior

The decoder accepts inputs that contain a valid QOI header but do not contain enough chunks to encode the declared pixels.

Examples:

no_chunks_1x1.qoi
  size: 22 bytes
  qoi_read(): decode=ok

truncated_rgba_2x1.qoi
  size: 23 bytes
  qoi_read(): decode=ok

no_chunks_20000000x1_rgba.qoi
  size: 22 bytes
  qoi_read(): decode=ok
  observed RSS: about 80 MB in my local test

The last case is a 22-byte input that declares a large image and causes the decoder to allocate the full output buffer even though the file contains no actual pixel chunks.

Expected behavior

Malformed QOI streams that do not provide enough data for the declared image dimensions should be rejected.

The API documentation says:

Returns NULL if something went wrong.

I would expect truncated/no-chunk streams to return NULL rather than being accepted as successfully decoded images.

Impact

This can lead to surprising successful decoding of invalid QOI data and allows small files to trigger much larger memory allocations based on declared dimensions.

I did not observe a memory-safety crash, so this should be treated as a correctness/resource-amplification issue rather than a code execution issue.

Reproduction package

I attached a minimal reproduction package without binaries:

qoi-maintainer-repro_public_minimal.zip
SHA-256: A250578ABDFDCEC2EDCB3A965A411E10FDD427659ACB22ED67E9573C7603D8B3

The package contains:

README
PoCs
reproduction scripts
logs
fuzzing summary
suggested validation direction
SHA256SUMS.txt

I can provide the full reproduction package with convenience binaries if useful.

qoi-maintainer-repro_public_minimal.zip