内存写越界导致破环堆结构引起的崩溃问题定位经验[如报错malloc(): memory corruption或free(): invalid next size]
前段时间开发的一个后端C模块上线后,线上出core,初始时,因为訪问压力不大,所以崩溃是上线3天左右出现的。当时用gdb跟进调用堆栈并检查源代码,发现出core位置的代码沒有啥问题。因为当时开发任务较重,且该模块不保存状态(崩溃重新启动不影响对外服务),所以沒有深入跟进。后来随着client版本号逐渐放量导致訪问压力上升,噩梦開始了。。。
        该模块会不定时core掉,并且差点儿每次崩溃时的调用堆栈都不一样,关键是最后几层堆栈非常多都位于差点儿不可能出问题的代码中,比方库函数或厂里的公共库。
        好在在众多core文件里发现规律:每次基本都是在对内存动态操作时挂掉,比方malloc/realloc/free/new/delete都引起了崩溃。并且幸运的是,崩溃进程还是输出了一些关键信息,比方以下这些(这些是在不同的崩溃时刻分别输出的):
*** glibc detected *** malloc(): memory corruption: 0x0000002a95c1ff10 ***
*** glibc detected *** double free or corruption (out): 0x0000000000f0d910 ***
*** glibc detected *** free(): invalid next size (normal): 0x0000002a96103b00 ***
*** glibc detected *** free(): invalid next size (fast): 0x0000000000f349d0 ***
*** glibc detected *** corrupted double-linked list: 0x0000002a95f062e0 ***
        从上面的日志也能够看到,每次引起崩溃的直接原因都可能不同。用gdb又细致查看core文件发现,有时进程是收到SIGABRT信号后退出,有时又是收到SIGSEGV信号后退出。
        由此,基本定位了崩溃原因:内存訪问越界导致破坏了heap的数据结构。用valgrind在线下环境启动进程,试图重现崩溃或定位越界訪问的代码,遗憾的是,脚本压了1个小时也没出现崩溃,而valgrind的输出报告也沒有越界代码位置的提示。
        终于,细致检查源代码后发现,在某个回调函数中,new出来的buffer接收完通过http post方式发送过来的2进制数据后,我又多写了1行代码,相似于:recv_buf[data_len] = '\0',导致越界多写1个字节,终于引起各种莫名其妙的内存崩溃。
经验教训:
        1)调用堆栈信息对定位问题帮助非常大,但也不可尽信。比方这次遇到的情况,每次出core的调用堆栈差点儿都不一样,并且最后几层栈帧都是不可能出现故障的库函数或久经考验的公司公共库,这样的情况下,思维须要跳出局部,在更高的层次寻找规律或原因
        2)一旦定位崩溃属于堆内存读写越界问题,就细致检查自己的代码吧,因为库函数或公共库出问题的概率太小了,所以不要存在侥幸心理,这个时候,盲目的自信要不得
        3)本来自觉得对内存操作已经非常小心了,没想到还是在想当然的瞬间写下犯错的代码,导致终于花费非常多时间和精力去“捉虫”。只是好在跟进崩溃的过程中添加了一点分析/定位问题的经验,也算有些收获吧
=============== EOF =================
内存写越界导致破环堆结构引起的崩溃问题定位经验[如报错malloc(): memory corruption或free(): invalid next size]的更多相关文章
- 【JVM之内存与垃圾回收篇】堆
		堆 堆的核心概念 堆针对一个 JVM 进程来说是唯一的,也就是一个进程只有一个 JVM,但是进程包含多个线程,他们是共享同一堆空间的. 一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的 ... 
- 【CPU微架构设计】分布式多端口(4写2读)寄存器堆设计
		寄存器堆(Register File)是微处理的关键部件之一.寄存器堆往往具有多个读写端口,其中写端口往往与多个处理单元相对应.传统的方法是使用集中式寄存器堆,即一个集中式寄存器堆匹配N个处理单元.随 ... 
- C++内存管理4-Windows编程中的堆管理(转)
		1 引言 在大多数Windows应用程序设计中,都几乎不可避免的要对内存进行操作和管理.在进行大尺寸内存的动态分配时尤其显的重要.本文即主要对内存管理中的堆管理技术进行论述. 堆(Heap)实际是位于 ... 
- Leetcode Lect3 内存中的栈空间与堆空间
		内存中的栈空间与堆空间 我们通常所说的内存空间,包含了两个部分:栈空间(Stack space)和堆空间(Heap space) 当一个程序在执行的时候,操作系统为了让进程可以使用一些固定的不被其他进 ... 
- 【pwn】学pwn日记(堆结构学习)
		[pwn]学pwn日记(堆结构学习) 1.什么是堆? 堆是下图中绿色的部分,而它上面的橙色部分则是堆管理器 我们都知道栈的从高内存向低内存扩展的,而堆是相反的,它是由低内存向高内存扩展的 堆管理器的作 ... 
- Android内存机制分析1——了解Android堆和栈
		//----------------------------------------------------------------------------------- Android内存机制分析1 ... 
- 堆结构的优秀实现类----PriorityQueue优先队列
		之前的文章中,我们有介绍过动态数组ArrayList,双向队列LinkedList,键值对集合HashMap,树集TreeMap.他们都各自有各自的优点,ArrayList动态扩容,数组实现查询非常快 ... 
- RocketMQ_问题_启动报错,修改堆内存大小
		1.启动broker报错 虚拟机内存小,导致虚拟机中的JVM内存小,进而在启动broker时分配JVM内存遇到问题 查询网上得知,查看/usr/local/rocketmq-all-4.3.0/dis ... 
- Libheap:一款用于分析Glibc堆结构的GDB调试工具
		Libheap是一个用于在Linux平台上分析glibc堆结构的GDB调试脚本,使用Python语言编写. 安装 Glibc安装 尽管Libheap不要求glibc使用GDB调试支持和 ... 
随机推荐
- Android中Activity的生命周期图
- vue 访问子组件示例 或者子元素
			1.子组件 <base-input ref="usernameInput"></base-input> this.$refs.usernameInput 2 ... 
- <p>在静态类junit.framework.Assert或者静态类org.junit.Assert中存在下面几个方法</p>
			在静态类junit.framework.Assert或者静态类org.junit.Assert中存在下面几个方法 1.assertEquals()方法,用来查看对象中存的值是否是期待的值,与字符串比較 ... 
- 【HTML5】元素<script>与<noscript>的使用
			功能描述 在新建的页面中增加一个文本框"txtContent"和一个按钮"请点击我":当单击按钮时.通过页面中加入的JavaScript脚本获取为文本框中的内容 ... 
- src-resolve: 无法将名称 'extension' 解析为 'element declaration' 组件。
			activiti流程部署时,出现“src-resolve: 无法将名称 'extension' 解析为 'element declaration' 组件.”错误. 出错原因:项目所在路径中有中文. 
- SELECT * INTO xx FROM x0
			insert into a select * from b:--向存在表中插入数据,如果不存在表a报错. select * into a from b:--创建新表的同时插入数据,如果表a存在,报错. ... 
- 利用 apache bench 模拟并发请求
			示意代码如下 ab -n 1000 -c 10 http://127.0.0.1/ -n 指的是总的请求,默认值是 1 -c 指的是并发数,默认值是 1 -t 指的是测试的总时间,测试所进行的最大秒数 ... 
- [译]GLUT教程 - 子菜单
			Lighthouse3d.com >> GLUT Tutorial >> Pop-up Menus >> Sub Menus 上一节我们介绍了如何创建普通菜单和如果 ... 
- Mybatis-There is no getter for property named 'id' in 'class java.lang.String'
			<mapper namespace="cn.telchina.standard.mapper.SysOrgnMapper"> <!-- <