zlib uncompress error

| 分类 Web  | 标签 extension  chrome 

1 问题描述

有段代码,示例如下:

1: uint32_t dstLen = 1024*80;
2: unsigned char out = malloc(dstLen);
3: memset(out, 0, dstLen);
4: 
5: err = uncompress(out, &dstLen, com, dl);

当将 Target 设置成 32 位时,工作正常。当 Target 为 64 位时, Debug 版工作正常,Release 版会报错,错误码为 Z_BUF_ERROR 。查阅 uncompress 的文档,返回值的解释如下:

1: /* uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
2:    enough memory, Z_BUF_ERROR if there was not enough room in the output
3:    buffer, or Z_DATA_ERROR if the input data was corrupted.
4: */

看起来是因为输出区不够大,是这样么?

2 问题的解决

查看64位的编译log,发现其中有提示:

warning: passing argument 2 of ‘uncompress’ from incompatible pointer type [enabled by default] note: expected ‘uLongf *’ but argument is of type ‘uint32_t *’

按照编译器的提示,更改如下:

1: uint32_t dstLen = 1024*80;
2: unsigned char out = malloc(dstLen);
3: memset(out, 0, dstLen);
4: uLongf tmpLen = dstLen;
5: err = uncompress(out, &tmpLen, com, dl);

更改之后,问题解决了。但为什么 32 位下没有问题?而且为什么 64 位 Debug 版没有问题, Release 版有问题?

3 问题原因

翻开 zlib 的源码看了一下,=uncompress= 开始有一段检查:

1: stream.next_out = dest;
2: stream.avail_out = (uInt)*destLen;
3: if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;

这里 zlib 将传入的 destLen 强转成了 uInt ,并随后又强转成了 uLong 并和传入的 destLen 做了个比较,如果不相等,则返回 Z_BUF_ERROR

这里有些地方值得注意:

uInt 在 32/64 位下均为 32 bits, uLongf 在 32 位OS 下为 32 bits, 64 位下为 64 位。 64 位下,如果 destLen 是指向 uint32 的指针,如 line 1 所示,line 2 & 3 所读到的范围将超出 desLen 的真正范围,如果没有发生 SegmentFault,则读到的值有可能并不正确。

2 行还好,最后的强转会将高位的 4 字节丢掉,得到的值和 line 1 的值一样,但是对于第 3 行,得到的值很可能会不一样。这就解释了为什么 64 位下有问题,而 32 位下没有问。

那 Debug 和 Release 的区别呢?

Release 版相对 Debug 打开了编译器的优化,优化后生成的二进制文件更小,执行也更快。Release 版生成的可执行文件在运行过程中使用的栈貌似更加“紧凑”。没研究过编译器,不知道怎么表达更合一些,还是看下例子吧:

1: uint32_t* ptr = &dstLen;
2: fprintf(stderr, "ptr: %p, value: %"PRIX32"\n", ptr, *ptr);
3: ptr++;
4: fprintf(stderr, "ptr: %p, value: %"PRIX32"\n", ptr, *ptr);

这段代码中, 4 中尝试去打印栈对象的指针的“下一个”( ptr++ ),Debug 版上,打印出的结果大多为 0 ,而 Release 版则大多非零。该非零值作为 64 位的 uLong 的高 4个 Byte,得到的值也就和传入的值不再相同,从而 Line 3 返回的 Z_BUF_ERROR

所以 Relase 版有问题,Debug 版的没问题其实只能算是巧合了。

zlib 为什么加了这段转换和检查 ?
不知道,看起来像是想强制检查入参的类型是否与自己的期望相符?


上一篇     下一篇