zlib 压缩输出缓冲区 overflow 问题

问题

后台服务传包太大时,我们框架可以使用 zlib 库对响应进行压缩;在这次服务调试过程中,使用 zlib compress2Z_BEST_COMPRESSION 模式进行压缩时,报 Z_BUF_ERROR(-5) 错误

分析

bool Codec::ZlibCompress(const std::string& src , std::string& compr_str)
{
    int max_size = src.size();
    if(max_size == 0)
    {
        return false;
    }
    DataBuffer<char> compr(max_size);
    memset(compr, 0, max_size);
    uLongf compr_len = max_size;
    int res = compress2((Bytef*)compr.Get(),&compr_len,(const Bytef*)(src.c_str()),src.size(),9);
    if(res != Z_OK)
    {
        return false;
    }

    compr_str = Base64Encode((const void*)compr,compr_len);
    return true;
}

这里 compress2 的输出缓冲区大小设置成为待压缩原串的长度了,在压缩数据太小或随机性很大等情况下,压缩得到的数据可能会比原来的大,导致 Z_BUF_ERROR;

下面的数据是对随机字符的压缩情况,src_size 为原串大小,compr size 为压缩后长度,说明在字符很随机的情况下,压缩完再使用 Base64Encode(Base64 编码数据会增大 1/3),长度一般都会变大。

解决

int compressBound(uLong sourceLen);

zlib 提供了一个 compressBound 方法,用于估算调用一次 compress2()/compress(),用于压缩 sourceLen 长度数据所需输出缓冲区的最大大小。

但是框架压缩后,对压缩串进行 Base64 编码(Base64 编码数据会增大 1/3),最终的输出缓冲区大小应该至少设置为 compressBound(sourceLen) * 4 / 3,因此改后的代码为:

bool Codec::ZlibCompress(const std::string& src , std::string& compr_str)
{
    int src_size = src.size();

    // 原串则不进行压缩,且压缩输出串置空
    if(0 == src_size)
    {
        compr_str = "";
        return true;
    }

    /*
        经过测试,在 best_compress(9) 的模式下,由随机字符构成的原串,压缩输出串有可能比原串长度大
        由随机字符构成的原串,长度在 0-40k 情况下,output_buffer_size / compressBound() * 100 最大为 133.33(best_compress 和 best_speed 下此值一样)
        为保证输出缓冲区足够大,缓冲区大小设置为 compressBound() * 2
    */
    int max_size = compressBound(src_size) * 2;

    // 如果得到的输出缓冲区大小不大于 0,则返回压缩失败
    if(max_size <= 0)
    {
        return false;
    }

    DataBuffer<char> compr(max_size);
    uLongf compr_len = max_size;
    int res = compress2((Bytef*)compr.Get(),&compr_len,(const Bytef*)(src.c_str()),src_size,9);
    if(res != Z_OK)
    {
        return false;
    }

    compr_str = Base64Encode((const void*)compr,compr_len);
    return true;
}

bool Codec::ZlibDecompress(const std::string& base64_src , std::string& decmpr_str)
{  
    int size = base64_src.size();

    if(0 == size)
    {
        decmpr_str = "";
        return true;
    }

    DataBuffer<char> src(base64_src.size());
    int base64_res = Base64Decode((const char*)(base64_src.c_str()),base64_src.size(),(void*)src,&size);
    if(base64_res != 0)
    {
        return false;
    }
    int max_size = size*30;
    DataBuffer<char> decompr(max_size);
    uLongf decompr_len = max_size;
    uLong src_size = size;
    int res = uncompress2((Bytef*)decompr.Get(),&decompr_len,(const Bytef*)src.Get(),&src_size);
    if(res != Z_OK)
    {
        return false;
    }
    decmpr_str = std::string(decompr , decompr_len);
    return true;
}

参考

compressBound
http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/zlib-compressbound-1.html

In what situation would compressed data be larger than input?
https://stackoverflow.com/questions/16992754/in-what-situation-would-compressed-data-be-larger-than-input

zlib详细基础教程
https://www.0xaa55.com/thread-442-1-1.html

posted @   cposture  阅读(1350)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
levels of contents
点击右上角即可分享
微信分享提示