Bug:从map中取值时崩溃
Bug:从map中取值时崩溃
总结
当 paraMap 中不包含元素时,如下代码会返回 true:
1
2
3
4
5
6
7
bool testFunc(std::map paraMap)
{
if(0 > paraMap.size() - 1) {
return false;
}
return true;
}
现象:
- AOS2 服务崩溃,log 中没啥参考价值,直接看 dmp 日志:
0x008E067E (TKAOS2Service.exe) (10.30.126.106_TKAOS2Service250806-201609.dmp 中)处有未经处理的异常: 0xC0000005: 读取位置 0x0054005C 时发生访问冲突。
- 根据 dmp 日志定位到如下函数,是从 it 中取值时崩溃:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// 获取map中第num个对象的信息,返回一个string std::string CAOS2DevID::GetClsInfo(int num) const { if (num < 0 || num > 3) { return ""; } if(num > m_mapDevField.size() -1) { // 当 map 为空时,此处不会直接 return return ""; } auto it = m_mapDevField.begin(); std::advance(it, num); std::string strField = GetStrFromField(it->first); // dmp日志显示在这里崩溃 std::string strID = it->second.strID; std::string strErrCode = std::to_string(it->second.ecCode); std::string strECMsg = aos2::ErrorCode2Str(it->second.ecCode); std::ostringstream oss; oss << "设备标识[" << strField << "]:" << strID << ", 错误码为: " << strErrCode << ", 错误信息为: [" << strECMsg << "]"; return oss.str(); }
问题与原因:
- 经复现,发现当
m_mapDevField为空时,如下代码不会直接 return!继续往下执行就会出现崩溃。1 2 3
if(num > m_mapDevField.size() -1) { return ""; }
分析:
- std::map 的 size() 返回值是无符号类型(通常是 size_t)。当
m_mapDevField为空时: m_mapDevField.size()的值为 0m_mapDevField.size() - 1会发生无符号数下溢,结果变为该类型的最大值(例如 size_t 为 32 位时是 4294967295)- 此时原代码的判断条件
if(num > m_mapDevField.size()-1)就会变成:1 2
// 当size() == 0时,等价于: if(num > 4294967295) // num的取值是0~3,显然不成立,因此不会直接return
- 条件判断失效,代码会继续向后执行
std::advance(it, num),但此时容器为空,迭代器it初始就是end(),再向前移动 num 步会导致迭代器越界到非法内存区域,最终访问it时触发内存访问冲突。
解决方案:
1
2
3
4
5
6
7
8
9
// 增加对迭代器的保护:
if (it == m_mapDevField.end()) { // 确认迭代器的有效性
return "";
}
// 或:
// 修改判断逻辑为:
if(num + 1 > m_mapDevField.size()) {
return "";
}
思考:
- 在处理容器迭代器时,直接检查迭代器是否有效比依赖 size() 计算更安全
- 在会涉及到隐式转换的地方,应该尤其注意数据类型
本文由作者按照
CC BY 4.0
进行授权