记一次空迭代器导致的崩溃分析

一. 崩溃代码:

class EasySelect::Impl {
public:
	Impl() = default;
	std::vector<int> waitForReadable ();
	void addFd (int fd);
	void removeFd (int fd);
	void stopWait ();

private:
	std::vector<int> m_fds;
	std::mutex m_fdsMutex;
	std::mutex m_pipeMutex;
	std::pair<int, int> m_pipe;
	void openPipe ();
	void closePipe ();
};

崩溃发生在removeFd()成员函数:

void
EasySelect::Impl::removeFd (int fd)
{
	std::lock_guard<std::mutex> lock (m_fdsMutex);
	m_fds.erase (std::find (m_fds.begin(), m_fds.end(), fd));
}

正确的做法是一定要对迭代器判空的,所以应该这样:

void
EasySelect::Impl::removeFd (int fd)
{
	std::lock_guard<std::mutex> lock (m_fdsMutex);
	auto fdIt = std::find (m_fds.begin(), m_fds.end(), fd);
	if (fdIt == m_fds.end())
		return;
	m_fds.erase (fdIt);
}

二、 崩溃原因

erase()时崩溃,原因是没有对迭代器没有判空,erase()一个尾后迭代器导致崩溃。

知道了结果后自然很简单,但是在不明原因时却不太好排查。
因为总是以上帝视角来分析并不能提高我们的debug能力,所以这里以有限的视角来进行反推。

tip:不要放过每一次机会,这样在以后遇到更复杂的场景时才能应对。

三、 堆栈分析

这里用vscode + gdb来分析。

核心转储文件的生成见另一篇博客:gdb调试核心转储文件

1. 观察崩溃堆栈

$ gdb ./testPublic core

输入bt命令(或者info stackwhere

在这里插入图片描述

#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:273
#1  0x000055a8f7a03164 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<int> (__first=0x4, __last=0x0, __result=0x0)
    at /usr/include/c++/12/bits/stl_algobase.h:431
#2  0x000055a8f7a02fed in std::__copy_move_a2<true, int*, int*> (__first=0x4, __last=0x0, __result=0x0) at /usr/include/c++/12/bits/stl_algobase.h:495
#3  0x000055a8f7a02dce in std::__copy_move_a1<true, int*, int*> (__first=0x4, __last=0x0, __result=0x0) at /usr/include/c++/12/bits/stl_algobase.h:522
#4  0x000055a8f7a02aed in std::__copy_move_a<true, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > > (__first=<error reading variable: Cannot access memory at address 0x4>, __last=non-dereferenceable iterator for std::vector, 
    __result=non-dereferenceable iterator for std::vector) at /usr/include/c++/12/bits/stl_algobase.h:529
#5  0x000055a8f7a028c9 in std::move<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > > (__first=<error reading variable: Cannot access memory at address 0x4>, __last=non-dereferenceable iterator for std::vector, 
    __result=non-dereferenceable iterator for std::vector) at /usr/include/c++/12/bits/stl_algobase.h:652
#6  0x000055a8f7a02232 in std::vector<int, std::allocator<int> >::_M_erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/vector.tcc:179
#7  0x000055a8f7a01c0c in std::vector<int, std::allocator<int> >::erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/stl_vector.h:1530
#8  0x000055a8f7a00df4 in EasySelect::Impl::removeFd (this=0x55a8f87e22d0, fd=0) at /home/sixqaq/ftp/ftp/public/src/EasySelect.cpp:46
#9  0x000055a8f7a0151f in EasySelect::Impl::closePipe (this=0x55a8f87e22d0) at /home/sixqaq/ftp/ftp/public/src/EasySelect.cpp:109
#10 0x000055a8f7a00e3a in EasySelect::Impl::stopWait (this=0x55a8f87e22d0) at /home/sixqaq/ftp/ftp/public/src/EasySelect.cpp:52
#11 0x000055a8f7a01682 in EasySelect::stopWait (this=0x7ffc4ef5ed50) at /home/sixqaq/ftp/ftp/public/src/EasySelect.cpp:132
#12 0x000055a8f7a06643 in testEasySelect () at /home/sixqaq/ftp/ftp/public/test/src/testEasySelect.cpp:24
#13 0x000055a8f7a05a14 in main () at /home/sixqaq/ftp/ftp/public/test/src/main.cpp:13

先粗看崩溃位置,发现是在std::vector<int>::erase()时失败,留意一下那些敏感参数值:error、null、none-reference之类的,包括可疑的内存地址。

在vscode终端里的堆栈信息,用Ctrl + Click都是可以跳转到崩溃代码的。

2. 作出猜想

先作出初步的猜想。

这个崩溃的是段错误,起初以为是并发访问的问题,后来验证不是。而且能够稳定复现,这也反映大概不是由于数据竞争。

这里其实也是我的失误,当看到空指针、空引用的那一刻起,就应该立刻追溯这些空值的传递。

3. 梳理上下文

#1堆栈开始看。

#1  0x000055a8f7a03164 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<int> (__first=0x4, __last=0x0, __result=0x0)
    at /usr/include/c++/12/bits/stl_algobase.h:431

在这里插入图片描述

注意到这里的lastresult都是空指针,有必要验证一下它们的合法性。
看到这个复杂的模板不用怕,直接丢给GPT,不过这里没必要,确实没有什么有效信息,__builtin_memmove(),如果你C语言基础还行的话,应该能猜到它是类似于memove()的,不清楚也没关系。
在这里插入图片描述

这里看不出什么有效信息,所以我们继续看参数是怎么传递过来的,特别是lastresult这两个空的指针。

在这里插入图片描述

#0  __memcpy_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:273
#1  0x000055a8f7a03164 in std::__copy_move<true, true, std::random_access_iterator_tag>::__copy_m<int> (__first=0x4, __last=0x0, __result=0x0)
    at /usr/include/c++/12/bits/stl_algobase.h:431
#2  0x000055a8f7a02fed in std::__copy_move_a2<true, int*, int*> (__first=0x4, __last=0x0, __result=0x0) at /usr/include/c++/12/bits/stl_algobase.h:495
#3  0x000055a8f7a02dce in std::__copy_move_a1<true, int*, int*> (__first=0x4, __last=0x0, __result=0x0) at /usr/include/c++/12/bits/stl_algobase.h:522
#4  0x000055a8f7a02aed in std::__copy_move_a<true, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > > (__first=<error reading variable: Cannot access memory at address 0x4>, __last=non-dereferenceable iterator for std::vector, 
    __result=non-dereferenceable iterator for std::vector) at /usr/include/c++/12/bits/stl_algobase.h:529
#5  0x000055a8f7a028c9 in std::move<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > > (__first=<error reading variable: Cannot access memory at address 0x4>, __last=non-dereferenceable iterator for std::vector, 
    __result=non-dereferenceable iterator for std::vector) at /usr/include/c++/12/bits/stl_algobase.h:652
#6  0x000055a8f7a02232 in std::vector<int, std::allocator<int> >::_M_erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/vector.tcc:179
#7  0x000055a8f7a01c0c in std::vector<int, std::allocator<int> >::erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/stl_vector.h:1530

#2#3#4#5也没什么好看的,都是firstlastresult传来传去,名字看起来也类似。

直到#6

在这里插入图片描述
这个模板函数_M_erase(iterator __position)只接收一个__position参数,调用了_GLIBCXX_MOVE3(__position + 1, end(), __position);

比对#5#6,发现:

  1. __position+1就是first
  2. end()就是last
  3. __position就是result

那么,end()(也就是last)为空当然是很合理的事。
至于firstresult的值,我们要再进一步分析它们的作用。

在这里插入图片描述

#6  0x000055a8f7a02232 in std::vector<int, std::allocator<int> >::_M_erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/vector.tcc:179
#7  0x000055a8f7a01c0c in std::vector<int, std::allocator<int> >::erase (this=0x55a8f87e22d0, __position=non-dereferenceable iterator for std::vector)
    at /usr/include/c++/12/bits/stl_vector.h:1530

__position就是#7erase()中传入的迭代器值。

把这个_M_erase()的代码丢给GPT,它会快速帮你分析出参数的作用:

GLIBCCXX_MOVE3是通过将[__position+1, end()]这个区间的元素都移动到__position地址处,从而实现了“删除”效果。

那么就很明确为什么崩溃了,因为传入的__position为空,__position+1也就是0x4地址是一个无法保障的地址,自然就会产生段错误了。

四、吸取教训

迭代器和指针一定要判空。

其实这个判空前面是一直留了个心眼的,偷懒没加,隔太久忘了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/538118.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL数据导出导出的三种办法(13/16)

数据导入导出 基本概述 目前常用的有3中数据导入与导出方法&#xff1a; 使用mysqldump工具&#xff1a; 优点&#xff1a; 简单易用&#xff0c;只需一条命令即可完成数据导出。可以导出表结构和数据&#xff0c;方便完整备份。支持过滤条件&#xff0c;可以选择导出部分数据…

python知识点汇总(十一)

python知识点总结 1、当Python退出时&#xff0c;是否会清除所有分配的内存&#xff1f;2、Python的优势有哪些&#xff1f;3、什么是元组的解封装4、Python中如何动态获取和设置对象的属性&#xff1f;5、创建删除操作系统上的文件6、主动抛出异常7、help() 函数和 dir() 函数…

数据结构-----枚举、泛型进阶(通配符?)

文章目录 枚举1 背景及定义2 使用3 枚举优点缺点4 枚举和反射4.1 枚举是否可以通过反射&#xff0c;拿到实例对象呢&#xff1f; 5 总结 泛型进阶1 通配符 ?1.1 通配符解决什么问题1.2 通配符上界1.3 通配符下界 枚举 1 背景及定义 枚举是在JDK1.5以后引入的。主要用途是&am…

加速杂交水稻走向世界 政协委员建议在湖南设立一“协会”一“中心”

中新网北京3月8日电 (刘曼)针对中国杂交水稻海外“飘香”的现象&#xff0c;全国政协委员、湖南省政协副主席、民盟湖南省委会主委何寄华建议&#xff0c;在湖南建立杂交水稻国际合作交流协会、设立杂交水稻国际科技合作技术转移中心&#xff0c;支持杂交水稻走向世界。 全国政…

计算机基础知识-第7章-程序的本质(2)——算法与数据结构概论

一、算法数据结构程序 提出这一公式并以此作为其一本专著的书名的瑞士计算机科学家尼克劳斯沃思&#xff08;Niklaus Wirth&#xff09;由于发明了多种影响深远的程序设计语言&#xff0c;并提出结构化程序设计这一革命性概念而获得了1984年的图灵奖。他是至今惟一获此殊荣的瑞…

Python爬取链家数据

技术&#xff1a;requests、BeautifulSoup、SQLite 解析页面&#xff0c;存数据到SQLite数据库&#xff0c;到时候你用navicat导出成csv什么的就行 1、确定城市 以天津为例&#xff0c;网页是https://tj.lianjia.com/ershoufang/rs/ 把上面这些地区名字复制 2、爬取数据内容…

三天做完pandas数据分析50题第一天

三天做完pandas数据分析50题第一天 第1题 将python的list转换为Series第2题 将字典转换为Series第3题 将Series转换成python的list第4题 使用numpy创建series。第5题 如何为Series添加新的元素&#xff1f;第6题 使用字典创建DataFrame第7题 给DataFrame设置索引列第8题 生成一…

每日一题---OJ题: 合并两个有序链表

嗨!小伙伴们,好久不见啦! 今天我们来看看一道很有意思的一道题---合并两个有序链表 嗯,题目看上去好像不难,我们一起画图分析分析吧! 上图中,list1有3个结点,分别为1,2,3 ; list2中有3个结点,分别为1,3,4, 题目要求我们要将这两个链表合并到一起,并且是升序,最后将链表返回。 …

光威神策PRO PCIe 5.0 SSD发布,国产固态硬盘进入10G俱乐部

全球半导体供应链的紧张局势和闪存资源的短缺让许多行业都面临着不小的压力 &#xff0c; 连带的也让消费者难以获取物美价廉的闪存产品 。但是&#xff0c;总有一些企业能够逆流而上&#xff0c; 像是 光威科技这家国产存储品牌&#xff0c; 最近就给国内消费者 带来了一个惊喜…

mybatis05:复杂查询:(多对一,一对多)

mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09; 文章目录 mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09;前言&#xff1a;多对一 &#xff1a; 关联 &#xff1a; 使用associatio…

Echarts-实现地图并轮播地图信息

目录 ./map-geojson/jinhua.json./CenterMap.vue./center.vue 使用地图组件效果 ./map-geojson/jinhua.json {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":330…

力扣—2024 春招冲刺百题计划

矩阵 1. 螺旋矩阵 代码实现&#xff1a; /** param matrix int整型二维数组 * param matrixRowLen int matrix数组行数* param matrixColLen int* matrix数组列数* return int整型一维数组* return int* returnSize 返回数组行数 */ int* spiralOrder(int **matrix, int matri…

网工内推 | 网络工程师,13薪,周末双休,华三、华为认证优先

01 路邦远大 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、配合市场销售人员&#xff0c;做好产品的售后服务工作&#xff1b; 2、负责项目方案安装调试指导以及日常客户使用培训&#xff0c;对客户提出的问题提出解决方案&#xff1b; 3、为客户提供专业、规范的…

图片作为背景的闪白问题,6种基础方案, 不会不知道吧

前言 关于【SSD系列】&#xff1a; 前端一些有意思的内容&#xff0c;旨在3-10分钟里&#xff0c; 500-1500字&#xff0c;有所获&#xff0c;又不为所累。 某天&#xff0c;发现有背景图片的弹出框&#xff0c;会出现闪白现象&#xff0c;这&#xff0c;兄弟们&#xff0c;你…

导入芯片原厂SDK Mirror源码到gerrit

下载镜像代码 repo init --mirror --repo-url ssh://xx/repo.git -u ssh://xx/manifests.git -m manifest.xml repo sync 创建AOSP project 对All Project权限修改 创建repo 在刚才下载的codebase根目录执行如下命令&#xff1a; repo forall -c echo $REPO_PROJECT; ssh -p 29…

C++ AVL树底层实现原理

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C知识分享⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 AVL 树 1.1 AVL树的概念 1.2 AVL树…

【Hadoop大数据技术】——Flume日志采集系统(学习笔记)

&#x1f4d6; 前言&#xff1a;在大数据系统的开发中&#xff0c;数据收集工作无疑是开发者首要解决的一个难题&#xff0c;但由于生产数据的源头丰富多样&#xff0c;其中包含网站日志数据、后台监控数据、用户浏览网页数据等&#xff0c;数据工程师要想将它们分门别类的采集…

永恒之蓝(ms17-010)复现

永恒之蓝 永恒之蓝&#xff08;Eternal Blue&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry勒索…

ARM架构麒麟操作系统安装配置Mariadb数据库

、安装配置JDK (1)检查机器是否已安装JDK 执行 java -version命令查看机器是否安装JDK,一般麒麟操作系统默认安装openjdk 1.8。 (2)安装指定版本JDK 如果麒麟操作系统默认安装的openjdk 1.8不符合需求的话,可以卸载机器安装的openjdk 1.8并按需安装所需的openjdk版本…

软件杯 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 前言1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 该项目较为新颖&#xff0c;适合作为竞…