C语言实现的RLE二进制文件压缩解压功能偶发故障求助
排查出的bug及修复方案
- 解压函数边界逻辑错误
RLEdecompress中循环写入的判断条件retIdx < compressedSize完全错误,compressedSize是压缩后的文件大小,解压写入的上限应该是传入的uncompressedSize参数,错误的边界会导致解压时提前终止写入或者写入逻辑异常。 - 二进制文件使用文本模式读写
所有fopen调用均使用了r/w文本模式,在Windows平台下文本模式会自动做换行符(0x0A与0x0D0A)的转换,且遇到0x1A字节会判定为文件结束提前终止读取。PDF是二进制格式,文件中存在大量非文本字节,只有碰到特殊字节时才会触发问题,这就是bug偶发的核心原因。 - 解压逻辑存在越界访问风险
当inIdx已经走到压缩数据的最后一个位置(等于compressedSize-1)时,data[inIdx] == data[inIdx + 1]的判断会访问缓冲区外的内存,属于未定义行为,若读到的垃圾值恰好等于当前字节,就会误判为连续重复标记,导致额外多出重复字节。
修复要点
- 所有二进制文件的fopen调用替换为二进制模式:把
r改为rb,w改为wb,示例:
FILE* fp = fopen(argv[1], "rb"); FILE* fpcompWrite = fopen("compressed", "wb+"); FILE* fpcompRead = fopen("compressed", "rb"); FILE* funcomp = fopen("uncompressed", "wb+");
- 修改RLEdecompress函数的错误逻辑,修复后的代码如下:
char* RLEdecompress(char* data, size_t compressedSize, size_t uncompressedSize, size_t extraAllocation) { char* ret = calloc(uncompressedSize + extraAllocation, 1); size_t retIdx = 0, inIdx = 0; while (inIdx < compressedSize) { ret[retIdx++] = data[inIdx]; // 先判断不会越界再做后续判断 if (inIdx + 1 < compressedSize && data[inIdx] == data[inIdx + 1]) { size_t occ = ((data[inIdx + 2]) - '0'); // 边界改为uncompressedSize for (size_t i = 1; i < occ && retIdx < uncompressedSize; i++) { ret[retIdx++] = data[inIdx]; } inIdx += 2; } inIdx += 1; } return ret; }
内容的提问来源于stack exchange,提问作者Samuele B.
相关产品推荐
相关产品推荐

