You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

编码自定义数据结构到字节缓冲区时出现缓冲区溢出,求助排查偏移计算问题

编码自定义数据结构到字节缓冲区时出现缓冲区溢出,求助排查偏移计算问题

我写了一个函数,负责把自定义的数据结构编码成字节缓冲区,最终返回实际写入的字节数。这个函数的执行流程如下:

  • 接收三个输入参数:字节缓冲区(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

火山引擎 最新活动