Serguey Asael Shinder: An input that costs more to read than to send is an attack, not a message
This week two denial-of-service bugs in Eclair, a Lightning Network node, were disclosed on Delving Bitcoin after being fixed in version 0.14.0. They are in Scala rather than Java, but the lesson is the same on any JVM, or anywhere else.
In the first, the node parsed a peer's feature bits one bit at a time and allocated several heap objects per bit. One maximum-length message cost it about 300 MB of memory churn. In the second, the node still accepted a compressed message format the protocol had retired years earlier, and decompressed it with no limit: a 64 KB message became 64 MB and about 17 million objects. In both cases anyone who could open a connection could make the node do vastly more work than they did.
The researcher who found the second one did so by asking a single question of the codebase: where can a peer make us spend far more than it spent? I think that question belongs in every review of code that reads untrusted input, and it is worth writing down because it is not the question we usually ask. We ask whether the input is valid. A 64 KB message that decompresses correctly is perfectly valid.

What the check looks like in practice
- Bound the output, not just the input. A size limit on the request is useless if the handler expands it. Decompression, decoding, JSON or XML parsing, image decoding and regular expressions can all multiply their input; each needs its own ceiling, enforced while it runs, not after.
- Count allocations per unit of input. A parser that allocates an object per bit, per character or per element turns a legal maximum-size message into a memory spike. Parse in bulk where the format allows it.
- Delete what the protocol no longer needs. The zlib path in the second bug existed only for compatibility with an encoding the specification had dropped four years earlier. Unused code paths are not neutral; they are attack surface that nobody is watching.
- Test with the biggest legal input. Most tests use typical messages. The interesting test sends the largest message the format allows, repeatedly, and checks that the service still answers a health check promptly - which is almost exactly how the fuzzer found the first bug.
None of this needs a security team. It needs the habit of asking, for every handler that touches bytes from outside, what the worst legal input costs, and whether the sender pays anything close to the same.