问题描述
DataMap::sub_iterator() 中计算起始索引时混用有符号/无符号类型,当 cnt > idx 时 st 为负值,隐式转换为 ulong 后发生回绕,导致循环访问超出 m_entrys 数组边界。
代码位置
文件: libdkapture/so/data-map.cpp
函数: DataMap::sub_iterator()
行号: 121-131
int DataMap::sub_iterator(ulong idx, void *buf, size_t bsz) const
{
idx &= (m_ent_cnt - 1);
idx += m_ent_cnt;
const AddrEntry &entry = m_entrys[idx];
ulong cnt = entry.dsz;
DKapture::DataType dt = KEY_DT(entry.hash);
long st = idx - cnt; // <--- 有符号 long
DEBUG(0, "sub_iterator st: %lu cnt: %lu bsz: %lu", st, cnt, bsz);
int ret = 0;
for (ulong i = st; i < idx; i++) // <--- 无符号 ulong,隐式转换
{
if (KEY_DT(m_entrys[i].hash) != dt)
{
continue;
}
// ... 访问 m_entrys[i]
}
}
触发条件
cnt(即 entry.dsz)大于 idx 时即可触发。虽然正常场景下 dsz 通常较小,但如果:
- 共享内存中的 AddrEntry 数据被意外破坏(如其他进程写入)
- push() 传入异常大的 dsz 值
- 并发场景下读到半写入状态的数据
则 st = idx - cnt 为负,for (ulong i = st; ...) 中 st 回绕为接近 ULONG_MAX 的极大值。此时循环条件 i < idx 几乎恒为 false,循环体不会执行;但若因某些巧合条件成立,则会访问远超出 m_entrys 数组范围的内存。
预期行为
即使 cnt > idx,也应安全处理,不依赖无符号回绕来"意外跳过"循环。
建议修复
在计算 st 前添加前置保护,将 cnt 限制在有效范围内:
ulong cnt = entry.dsz;
if (cnt > idx) {
cnt = idx;
}
long st = idx - cnt;
或统一使用有符号类型:
long cnt = entry.dsz;
if (cnt < 0) cnt = 0;
if (cnt > (long)idx) cnt = idx;
long st = (long)idx - cnt;
for (long i = st; i < (long)idx; i++)
环境信息
- 版本: 当前 master(commit bf1c3e4 及之后)
- 架构: 所有平台(x86_64、ARM64、Loong64、sw64)
- 编译器: 任何支持 C++17 的编译器均可复现此问题
附加说明
该问题在 DEBUG 日志中可能被掩盖——DEBUG(0, "sub_iterator st: %lu ...", st, ...) 使用 %lu 打印 long 类型,若 st 为负值,输出将表现为一个极大的正数,增加调试难度。
---
问题描述
DataMap::sub_iterator()中计算起始索引时混用有符号/无符号类型,当cnt > idx时st为负值,隐式转换为ulong后发生回绕,导致循环访问超出m_entrys数组边界。代码位置
文件:
libdkapture/so/data-map.cpp函数:
DataMap::sub_iterator()行号: 121-131