zlib: infinite loop allocating 4KB chunks in buf_add_compress()
When buf_add_compress() is called with done=1 on a zlib/gzip decompression state whose input stream is truncated (does not reach Z_STREAM_END), an infinite loop follows in `buf_add_compress(...)`. The issue is in `compress_zlib.c`, on error (`Z_BUF_ERROR`), if `done = 1` is set, it never errors if there are no more data to read in and so the caller gets `TOR_COMPRESS_BUFFER_FULL` value which has no exit path for the condition that `done = 1` leading to a infinite loop allocating 4KB each time. See `buf_add_compress()`, the `TOR_COMPRESS_BUFFER_FULL` branch in the switch: ```c switch (tor_compress_process(state, out: &next, out_len: &avail, in: &data, in_len: &data_len, finish: done)) { case TOR_COMPRESS_BUFFER_FULL: if (avail) { /* The compression module says we need more room * (TOR_COMPRESS_BUFFER_FULL). Start a new chunk automatically, * whether were going to or not. */ need_new_chunk = 1; } if (data_len == 0 && !done) { // <=== HERE HERE, done = 1 /* We've consumed all the input data, though, so there's no * point in forging ahead right now. */ over = 1; } break; } ``` For this to be triggered, there are conditions that is a compressed consensus or diff on disk that is truncated meaning it never contains `Z_STREAM_END`. A tor client requesting an **uncompressed** consensus in that state will trigger the bug. Truncated data on disk could happen due to disk corruption or partial write. It is not impossible to imagine these conditions sometimes for relays under heavy resource stress (ex: DDoS).
issue