Skip to content

fix: Bound decode_compressed to the length zlib actually wrote - #1623

Open
Mounika2456 wants to merge 2 commits into
brainboxdotcc:devfrom
Mounika2456:etf-compressed-length
Open

fix: Bound decode_compressed to the length zlib actually wrote#1623
Mounika2456 wants to merge 2 commits into
brainboxdotcc:devfrom
Mounika2456:etf-compressed-length

Conversation

@Mounika2456

Copy link
Copy Markdown

decode_compressed trusts the uncompressed size declared in the term header instead of the length uncompress() reports back, so a term that declares more than its zlib stream produces has the remainder decoded out of heap that was never written.

An ett_compressed term declaring 4096 bytes whose stream deflates to five (an ett_binary header announcing 4080 bytes of payload) comes back as a 4080 character string assembled from that memory; under ASAN the same input trips container-overflow in read_8_bits, since reserve() leaves the vector empty. zlibcontext::decompress already sizes its buffer and counts what inflate wrote, so this brings the term decoder in line with it. Covered by a new offline test that fails on master.

Code change checklist

  • I have ensured that all methods and functions are fully documented using doxygen style comments.
  • My code follows the coding style guide.
  • I tested that my change works before raising the PR.
  • I have ensured that I did not break any existing API calls.
  • I have not built my pull request using AI, a static analysis tool or similar without any human oversight.

@netlify

netlify Bot commented Aug 1, 2026

Copy link
Copy Markdown

Deploy Preview for dpp-dev ready!

Name Link
🔨 Latest commit d7532cd
🔍 Latest deploy log https://app.netlify.com/projects/dpp-dev/deploys/6a6dbb12d71ef40008ba93e5
😎 Deploy Preview https://deploy-preview-1623--dpp-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions github-actions Bot added documentation Improvements or additions to documentation code Improvements or additions to code. labels Aug 1, 2026
@braindigitalis

Copy link
Copy Markdown
Contributor

thanks for the pr but how are you triggering this in asan?

discord doesn't ever send ett_compressed term values?

Comment thread src/unittest/test.cpp Outdated
#include <dpp/unicode_emoji.h>
#include <dpp/restrequest.h>
#include <dpp/json.h>
#include <zlib.h>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unit tests should not directly import zlib

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped it. The test now carries the deflate stream as a fixed byte array instead of compressing at runtime, so it only needs etf_parser.

Comment thread src/dpp/etf.cpp Outdated
throw dpp::parse_exception(err_etf, "ETF compressed value: decompresson error");
}
/* uncompress() was handed the rest of the buffer as its input, so the term ends there */
offset = size;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this changes outer parsing behaviour and is not required, can you please justify why this is here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, that wasn't needed for the overread and I've put it back to offset += destinationSize, so outer parsing is untouched. What's left is just the bound on the inner parse: the vector is sized rather than reserved so its elements actually exist, and size comes from what uncompress() reported writing instead of the declared header value.

@braindigitalis

Copy link
Copy Markdown
Contributor

this seems to have come verbatim from discord/erlpack depository we based our erl parser on.

https://github.com/discord/erlpack/blob/2a4c0e832f3cd4e07c92d4baec326a631ed50f59/js/decoder.h#L318

as such discord themselves have the same bug in their code and also never noticed it because their erl does not send compressed types (wouldn't make sense, because the entire stream can be zlib compressed)

@Mounika2456

Copy link
Copy Markdown
Author

Not off a live gateway, no. I built the term by hand and passed it straight to etf_parser::parse, which is what the test does now:

131, 'P', 0x00, 0x00, 0x10, 0x00, 0x78, 0x9c, 0xcb, 0x65, 0x60, 0xe0, 0xff, 0x00, 0x00, 0x03, 0x34, 0x01, 0x6d

That's an ett_compressed declaring 4096 bytes over a stream that inflates to five. On dev it comes back as a 4080 character string built out of the vector's uninitialized capacity; under ASAN it trips container-overflow in read_8_bits first, since reserve() leaves size() at 0 so every read is outside the container.

And you're right about the lineage, the reserve() and the declared-size bound both came over from erlpack's decoder.h verbatim. Agreed it isn't reachable from the gateway if Discord never emits compressed terms, so this only matters for anything running untrusted ETF through the public parser. No objection if you'd rather close it on that basis.

Comment thread src/dpp/etf.cpp
const int ret = uncompress((Bytef*)outBuffer.data(), &destinationSize, (const unsigned char*)(data + offset), (uLong)(size - offset));

offset += sourceSize;
offset += destinationSize;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be source size? destination size is changed by zlib. we are supposed to increment offset by the compressed content size yes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code Improvements or additions to code. documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants