Skip to content

Fix infinite loop in DeComp() on empty input - #257

Draft
IrPgFKS0 wants to merge 1 commit into
BeamMP:masterfrom
IrPgFKS0:fix/decomp-empty-input-hang
Draft

Fix infinite loop in DeComp() on empty input#257
IrPgFKS0 wants to merge 1 commit into
BeamMP:masterfrom
IrPgFKS0:fix/decomp-empty-input-hang

Conversation

@IrPgFKS0

@IrPgFKS0 IrPgFKS0 commented Jul 5, 2026

Copy link
Copy Markdown

Problem

DeComp() in src/Compressor.cpp loops forever on empty input: a zero-length compressed body (e.g. a bare 4-byte ABG: frame, which a buggy or malicious peer can send) sizes output_buffer to 0, zlib returns Z_BUF_ERROR, and the grow step output_buffer.size() * 2 stays 0 forever — the 30 MB cap is never reached, so the receive loop spins at 100% CPU for the rest of the session.

Two adjacent issues in the same loop:

  • the cap check uses > while the buffer can only ever equal the value it's compared against after capping — changed to >=;
  • growth was uncapped, so a large legitimate payload over-allocated 15 → 30 → 60 MB before being rejected.

Fix

Reject empty input up front (return an empty buffer — the caller already treats a failed decompress as a dropped packet), cap the growth at the 30 MB limit, and check the cap with >=. The server's DeComp (in BeamMP-Server Common.cpp) already terminates on this input after its buffer-management rework; this brings the launcher to parity.

How this was found

Found during a robustness audit of a LAN fork of BeamMP (the same bug existed in the fork's server base and was reachable from any peer); the fix has been running in that fork for weeks of sessions.

Transparency

This fix comes from an AI-assisted fork: the bug was found and the patch written with the help of an AI coding tool (Claude), then tested by a human in real multiplayer sessions. Given this project's policy on AI-generated code, it's submitted as a draft for the maintainers to decide — happy to close it if that's not wanted, or for a maintainer to re-implement it independently.

🤖 Generated with Claude Code

A zero-length compressed body (e.g. a bare 4-byte "ABG:" frame, which a
buggy or malicious peer can send) sizes the output buffer to 0; zlib then
returns Z_BUF_ERROR forever because 0 * 2 stays 0 and the 30 MB cap is
never reached -- the receive loop spins at 100% CPU for the rest of the
session. Reject empty input up front.

Also cap the growth at the 30 MB limit (and check with >=) so the limit
is actually reachable and a large payload doesn't over-allocate
15 -> 30 -> 60 MB before being rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant