diff --git a/compress.cc b/compress.cc index 9d249a37..ef566c25 100644 --- a/compress.cc +++ b/compress.cc @@ -54,9 +54,33 @@ static std::vector do_compress(std::string_view input) { // on the compression size. +16 for Z_SYNC_FLUSH. std::vector buf(deflateBound(&strm, strm.avail_in) + 16); + // Compress data. It writes all compressed bytes except the last + // partial byte, so up to 7 bits can be held to be written to the + // buffer. strm.avail_out = buf.size(); strm.next_out = buf.data(); + CHECK(deflate(&strm, Z_BLOCK)); + // This is a workaround for libbacktrace before 2022-04-06. + // + // Zlib is a bit stream, and what Z_SYNC_FLUSH does is to write a + // three bit value indicating the start of an uncompressed data + // block followed by four byte data 00 00 ff ff which indicates that + // the length of the block is zero. libbacktrace uses its own zlib + // inflate routine, and it had a bug that if that particular three + // bit value happens to end at a byte boundary, it accidentally + // skipped the next byte. + // + // In order to avoid triggering that bug, we should avoid calling + // deflate() with Z_SYNC_FLUSH if the current bit position is 5. + // If it's 5, we insert an empty block consisting of 10 bits so + // that the bit position is 7 in the next byte. + // + // https://github.com/ianlancetaylor/libbacktrace/pull/87 + int nbits; + deflatePending(&strm, Z_NULL, &nbits); + if (nbits == 5) + CHECK(deflatePrime(&strm, 10, 2)); CHECK(deflate(&strm, Z_SYNC_FLUSH)); assert(strm.avail_out > 0);