zLib inflate() 在解压缩缓冲区时挂起

Posted

技术标签:

【中文标题】zLib inflate() 在解压缩缓冲区时挂起【英文标题】:zLib inflate() hangs while uncompressing buffer 【发布时间】:2015-05-23 18:42:41 【问题描述】:

我使用 zLib 1.2.7,取自 here。我已在 Microsoft Visual Studio 2010 中将其编译为静态库并将其添加到我的项目中。

我需要解压一些用 deflate 算法压缩的二进制数据。这里是:

unsigned char rawData[114] =

    0x00, 0x00, 0x00, 0x00, 0x15, 0x82, 0x05, 0x9D, 0x62, 0x91, 0x9A, 0x86, 0x26, 0xF3, 0x45, 0xBF, 
    0xE1, 0x69, 0x19, 0xA8, 0x80, 0x21, 0x08, 0x43, 0xF1, 0xEF, 0xCC, 0x01, 0x68, 0x4E, 0x3C, 0x06, 
    0x59, 0x6D, 0x90, 0xB2, 0x1F, 0xC3, 0x87, 0xC2, 0xBF, 0xC0, 0x90, 0xBE, 0x1F, 0x11, 0xB6, 0xD7, 
    0xB7, 0x06, 0x18, 0x32, 0x5F, 0x80, 0x8F, 0x09, 0xF1, 0x81, 0xF2, 0xB8, 0xC8, 0x9E, 0x71, 0xB7, 
    0xC9, 0x73, 0x7E, 0x88, 0x02, 0xD0, 0x9C, 0x65, 0xB0, 0x34, 0xD3, 0x97, 0x33, 0xE8, 0x80, 0x2D, 
    0x09, 0xC6, 0x5B, 0x03, 0x4D, 0x39, 0x73, 0x74, 0x1B, 0xAD, 0x19, 0x9D, 0xF0, 0xCA, 0x6F, 0xBD, 
    0xA4, 0xD5, 0x33, 0x6E, 0xDF, 0x1F, 0x11, 0x8A, 0xC5, 0xA2, 0x1C, 0x99, 0xE2, 0xDB, 0xBF, 0x7C, 
    0x0E, 0x8B
;

此数据块是从SPDY 会话中捕获的。这是我的解压代码(SPDY_dictionary_txt 可以在之前的链接中找到):

INT APIENTRY WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )

    z_stream zStream =  0 ;
    DWORD Length = sizeof(rawData);

    zStream.zalloc = Z_NULL;
    zStream.zfree  = Z_NULL;
    zStream.opaque = Z_NULL;

    inflateInit(&zStream);

    zStream.avail_in = Length;
    zStream.next_in = rawData;

    DWORD dwUsed = 0;
    DWORD dwSize = 5 * 1024;
    LPBYTE lpDecompressed = NULL;
    BOOL triedDictionary = FALSE;
    BOOL uncompressed = TRUE;

    lpDecompressed = (LPBYTE)calloc(dwSize, 1);

    do
    
        zStream.next_out = (LPBYTE)((DWORD_PTR)lpDecompressed + dwUsed);
        zStream.avail_out = dwSize - dwUsed;

        int zlib_rv = inflate(&zStream, Z_NO_FLUSH); // <-- THIS HANGS!

        if (zlib_rv == Z_NEED_DICT) 
        
            if (triedDictionary)
            
                uncompressed = FALSE;
                break;
            

            triedDictionary = TRUE;
            inflateSetDictionary(&zStream, SPDY_dictionary_txt, sizeof(SPDY_dictionary_txt));
        

        if (zlib_rv < 0)
        
            uncompressed = FALSE;
            break;
        

        dwUsed += dwSize - dwUsed - zStream.avail_out;
    
    while (zStream.avail_in);

    if(!uncompressed)
    
        OutputDebugString("Could not decompress buffer.\n");
    
    else
    
        OutputDebugString("Buffer was decompressed.\n");
    

    Sleep(1000);

    return 0;

我开始调试这段代码,发现它挂在第一个inflate() 调用上。这是什么?这是 zLib 中的错误还是我的代码有误?

【问题讨论】:

我在 Linux 上尝试了代码,它没有挂起。相反,zlib_rv 为 -3 (Z_DATA_ERROR),代码显示“无法解压缩缓冲区。” 好的,也许我捕获了错误的数据。但这意味着1.2.7版本存在可用于DoS攻击的bug。 你是对的。如果您确实正确使用了 zlib,则意味着您发现了 DoS 攻击媒介。我会考虑向 zlib@gzip.org 发送邮件,并详细解释您的发现,以便可以复制它们。这是一个足够重要的与安全相关的事情,你不应该把问题留在那里。 OK,但可能不是zLib的问题;我从另一个站点下载了它,它是 Visual Studio 项目,带有一些用于在 x86 和 x86_64 架构上编译它的技巧。 但是我已经一步步追踪了我的代码。如果我打两个OutputDebugString() 电话。分别在inflate() 之前和之后使用“message1”和“message2” - 它显示“message1”并且不显示“message2”。 【参考方案1】:

你下载的代码不是原来的zlib代码。它被某人修改为“没有警告和错误”的编译,并且在此过程中可能已经引入了错误。您需要从zlib.net 下载原始且正确的代码。例如,您下载的代码在 2014 年 5 月 31 日提交,日志消息“已更正 inflate.c 和 infback.c 中的无限循环错误”。你应该更加小心从陌生人那里下载代码。

请注意,问题中提供的数据不是 zlib 压缩的结果,在读取前两个字节时会立即被inflate() 拒绝,因为它甚至不是 zlib 标头。您必须使用其他一些输入数据来让您的代码“挂起”。您需要提供导致问题的实际数据,以便任何人都能够帮助您。

关于您的代码:您不需要在循环内更新next_outavail_out,因为inflate() 已经这样做了。当循环以dwSize - zStream.avail_out 退出时,您可以计算dwUsed。您还需要检查来自inflate() 的返回码,以确保它在完成后返回Z_STREAM_END,否则流不完整。您不应该在Z_BUF_ERROR 上中止,而是提供更多输出空间或更多输入。请参阅this example 了解如何使用inflate()deflate(),并阅读documentation in zlib.h。

【讨论】:

我尝试了 dwSize = 5 * 1024 * 1024 并且效果相同。是的,我的数据是错误的,因为我捕获了错误的数据。现在我明白了。但我跟踪我的代码 - 并看到执行在第一次调用 inflate() 后挂起,例如它不会返回并执行if (zlib_rv == Z_NEED_DICT)。反汇编显示类似l1: test edi, edi / jne l1 我的代码无法进入无限循环,因为我有一个错误检查 if (zlib_rv &lt; 0) 并在它之后中断。 好的,谢谢,我没有注意到该代码的最后更改,也没有克隆源代码,我从“所有下载”(2012 年)中获取了代码。我可以在 Microsoft Visual Studio 2010 for x86_64 中将原始 zLib 代码编译为静态库吗? 好的,谢谢你的回答,谢谢zLib!

以上是关于zLib inflate() 在解压缩缓冲区时挂起的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin.Mac 应用程序在与最终用户的文件系统交互时挂起

使用 GTMNSData+zlib 解压 NSData

zlib deflate:为啥它会累积短数据并且直到输入缓冲区已满才开始压缩?

zlib gunzip解压缩每个在同一文件上运行的不同缓冲区大小

C++学习(四八二)zlib的inflate

是啥导致 Eclipse 在与工作区断开连接时挂起?