编码自定义数据结构到字节缓冲区时出现缓冲区溢出,求助排查偏移计算问题
编码自定义数据结构到字节缓冲区时出现缓冲区溢出,求助排查偏移计算问题
我写了一个函数,负责把自定义的数据结构编码成字节缓冲区,最终返回实际写入的字节数。这个函数的执行流程如下:
- 接收三个输入参数:字节缓冲区(
char *类型)、缓冲区总大小、写入的起始偏移(比如偏移0x00就是从缓冲区开头开始写),同时传入要编码的目标数据结构 - 将所有唯一的
Unit_t数据收集到UnitSet对象的动态数组中 - 如果缓冲区剩余空间不足,就进行扩容(我测试时特意给了1字节的缓冲区,所以肯定会触发扩容逻辑)
- 先写入tiles的主体数据,再写入units的主体数据,最终输出的缓冲区结构是
[tiles数组] + [units数组]
遇到的问题
在最后编码units的循环里,处理到第5个unit时触发了缓冲区溢出错误。用ASAN和调试器排查后,确认错误就出在这段循环的代码里。我怀疑是偏移计算出了问题,因为写入文件的缓冲区内容要么全是垃圾数据,要么完全不符合预期,但我反复检查了类型转换和偏移计算,没发现哪里不对。我的预期逻辑是:跳到需要写入的字节位置,转换数据格式后按对应大小写入。
补充信息
TileSet_s结构本身没有问题,我已经为它编写了单元测试并验证通过- 最开始在tiles的编码循环里也出现过同样的溢出错误,后来修正了类型转换和偏移计算后,错误转移到了现在标注的这段units编码代码里——这也是我觉得大概率是整个偏移计算逻辑都有问题的原因
如果问题不是偏移或类型转换这么简单,我可以整理一个可复现的示例出来方便调试。
相关代码
#define TILE_ENCODED_SIZE ((sizeof(uint16_t) * 10) + 1) typedef double Unit_t; size_t TileSet_encode (struct TileSet_s *tileset, bytes_t **bytes, size_t bytes_offset, size_t bytes_size) { // next we collect the vertices, duplicated aren't stored so we need to collect // them and then calculate. struct UnitSet_s set; if (!UnitSet_init(&set)) { return 0; } for (TileSet_Size_t ti = 0; ti < tileset->count; ++ti) { // translation of this stuff: // iterate each tile and iterate each vertex of each tile. for (unsigned short vi = 0; vi < TILE_VERTICES_MAX; ++vi) { // dump the vertex into the set, if we fail terminate operation. if (UnitSet_add(&set, tileset->tilearray[ti]->tiledata.vertices[vi]) == UNIT_SET_NOMEM) { UnitSet_destroy(&set); return 0; } } } size_t size_tiles = tileset->count * TILE_ENCODED_SIZE; size_t size_vertices = sizeof(Unit_t) * set.list.length; size_t bytes_to_write = size_tiles + size_vertices; if ((bytes_size - bytes_offset) < bytes_to_write) { // now we know how much space the whole thing takes. // ensure to increase space if needed. bytes_size = (bytes_size - bytes_offset) + bytes_to_write; bytes_t *newbytes = realloc(*bytes, sizeof(**bytes) * (bytes_offset + bytes_size)); if (!newbytes) { // failed to increase bytes buffer. return 0; } *bytes = newbytes; } for (TileSet_Size_t ti = 0; ti < tileset->count; ++ti) { // encodes tiles. size_t tile_offset = bytes_offset + (ti * TILE_ENCODED_SIZE); struct Tile_s *tile = &tileset->tilearray[ti]->tiledata; *((TileSet_Id_t *) &((*bytes)[tile_offset])) = tileset->tilearray[ti]->id; for (size_t vi = 0; vi < TILE_VERTICES_MAX; ++vi) { ((uint16_t *) &((*bytes)[tile_offset + 1]))[sizeof(uint16_t) * vi] = (uint16_t) UnitSet_search(&set.list, tile->vertices[vi], 0, set.list.length - 1); } } for (size_t vi = 0; vi < set.list.length; ++vi) { // encode vertices. // ********************************************************** // ERROR HERE! Buffer Overflow at *bytes after the fifth unit !! ((Unit_t *) &((*bytes)[bytes_offset + size_tiles]))[sizeof(Unit_t) * vi] = set.list.array[vi]; // ********************************************************** } UnitSet_destroy(&set); return bytes_to_write; }
我尝试过的方法
- 用ASAN工具进行调试,定位到了错误发生的位置
- 调整了类型转换的写法,但问题依然存在
备注:内容来源于stack exchange,提问作者magg




