一次__libc_message的排查
abort的堆栈如下:
#0 0x00007f338dd60b55 in raise () from /lib64/libc.so.6
#1 0x00007f338dd620c5 in abort () from /lib64/libc.so.6
#2 0x00007f338dd9ee0f in __libc_message () from /lib64/libc.so.6
#3 0x00007f338dda4628 in malloc_printerr () from /lib64/libc.so.6
#4 0x000000000046abfe in OSMemory::Delete (inMemory=0x7f333e7fcf20) at OSMemory.cpp:278
#5 0x000000000046ac2f in operator delete (mem=0x7f333e7fcf20) at OSMemory.cpp:202
#6 0x000000000040e8a7 in __gnu_cxx::new_allocator<std::_List_node<CZMBuff*> >::deallocate (this=0x7f32a4a155a0, __p=0x7f333e7fcf20) at /usr/include/c++/4.3/ext/new_allocator.h:98
#7 0x000000000040e8cf in std::_List_base<CZMBuff*, std::allocator<CZMBuff*> >::_M_put_node (this=0x7f32a4a155a0, __p=0x7f333e7fcf20) at /usr/include/c++/4.3/bits/stl_list.h:318
#8 0x000000000040e9ef in std::_List_base<CZMBuff*, std::allocator<CZMBuff*> >::_M_clear (this=0x7f32a4a155a0) at /usr/include/c++/4.3/bits/list.tcc:79
#9 0x000000000049d579 in std::list<CZMBuff*, std::allocator<CZMBuff*> >::clear (this=0x7f32a4a155a0) at /usr/include/c++/4.3/bits/stl_list.h:1066
由于该段堆栈处于对象的销毁过程,所以应该是free的报错。根据对象本身的内存池设计,在malloc的时候,我们使用用户态的一个记录结构,记录了对象的长度。结构如下:
typedef struct
{
size_t ID;
size_t size;
}mem_hdr;
两个都是8位的长度,之后再跟实际的数据,也就是我调用my_malloc的时候,如果是传入24个字节,那么最终会向glibc的malloc提交40个字节,24+16.
查看free的异常的数据如下:
x /40xg 0x7f333e7fcf20 -64 0x7f333e7fcf20 就是上面堆栈中inMemory的值,这个值真正传给glibc的时候,会减去16而提交,即为0x7f333e7fcf0x7f333e7fcee0: 0x0000000000000000 0x0000000000000028
0x7f333e7fcef0: 0xffffffffffffffff 0xffffffffffffffff---------------------------------------这两列值明显异常,按道理应该是指针
0x7f333e7fcf00: 0xffffffffffffffff 0x00000000ffffffff--------------------------------
0x7f333e7fcf10: 0x0000000000000000 0x0000000000000028
0x7f333e7fcf20: 0x00007f32a57976e0 0x00007f333f7c08e0
0x7f333e7fcf30: 0x00007f32c25b2618 0x0000000000000035-------------这个转化为二进制就是110101 ,后面三位代表flag,#define PREV_INUSE 0x1,前面那个110000为48,表示长度
0x7f333e7fcf40: 0x0000000000000000 0x0000000000000028
0x7f333e7fcf50: 0x00007f330047a640 0x00007f333dbebfd0
0x7f333e7fcf60: 0x00007f32b04b81b8
这个就是应用程序的mem_hdr结构的id 和size,40转换成16进制就是0x28,0x28后面24个字节(3个指针)也应该
是用户数据,在本例中,分别就是 _List_node_base* _M_next; _List_node_base* _M_prev; _Tp _M_data; // 数据域,即标准模板类的管理结构。
正常的例子如下:
0x7f333e7fcf10: 0x0000000000000000 0x0000000000000028
0x7f333e7fcf20: 0x00007f32a57976e0 0x00007f333f7c08e0
0x7f333e7fcf30: 0x00007f32c25b2618 0x0000000000000035--------------最关键的是0x0000000000000035值被踩成了0x00000000ffffffff,如果只踩24字节而不是32字节,就不会glibc中报错了。
0x7f333e7fcf40: 0x0000000000000000 0x0000000000000028--------------下一个结构开始
分为两段来看,下面那段是正常的分配,上面那段是异常的分配,可以明显看出,上面0x1497650地址开始那段的32个字节,是有问题的。
我们回一下malloc的内存分配管理单元结构:
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
prev_size: If the previous chunk is free, this field contains the size of previous chunk. Else if previous chunk is allocated, this field contains previous chunk’s user data.
size: This field contains the size of this allocated chunk. Last 3 bits of this field contains flag information.
- PREV_INUSE (P) – This bit is set when previous chunk is allocated.
- IS_MMAPPED (M) – This bit is set when chunk is mmap’d.
- NON_MAIN_ARENA (N) – This bit is set when this chunk belongs to a thread arena.
Bins: Bins are the freelist datastructures. They are used to hold free chunks. Based on chunk sizes, different bins are available:
- Fast bin
- Unsorted bin
- Small bin
- Large bin
映射到内存示意图上如下图所示:
可以看到,我们每次malloc返回的指针并不是内存块的首指针,前面还有两个size_t大小的参数,对于非空闲内存而言size参数最为重要。size参数存放着整个chunk的大小,由于物理内存的分配是要做字节对齐的,所以size参数的低位用不上,便作为flag使用。
size被写坏,有两种结果。一种是free函数能检查出这个错误,程序就会先输出一些错误信息然后abort;一种是free函数无法检查出这个错误,程序便往往会直接crash。
根据最上面的堆栈推测,诱发bug的是前一种情况。
根据多个core文件的规律,发现每次踩的都是32字节,且踩的数据一模一样,都是:
0x1497650: 0xffffffffffffffff 0xffffffffffffffff
0x1497660: 0xffffffffffffffff 0x00000000ffffffff
换算成实际代码,有两种可能,一种是赋值为-1,一种是直接memcpy的时候是0xffffffffffffffff 。
切换到对应的堆栈,使用info register看寄存器,获取出来的CZMBuff是ok的,由于free的时候,是从标准模板类的双向循环列表中移除某个节点,
移除之后,调用free来释放对应的循环链表管理结构,此时出了问题。
标准模板类中的循环列表的结构,表示如下:
// ListNodeBase定义
struct _List_node_base {
_List_node_base* _M_next;
_List_node_base* _M_prev;
};
// ListNode定义
template <class _Tp>
struct _List_node : public _List_node_base {
_Tp _M_data; // 数据域
};
我们的数据域,其实是一个指向CZMBuff的二级指针,因为直接使用p不好打印链表中的内容,所以需要借助脚本: 创建一个脚本文件,里面包含如下内容(可以在网上下载:)
define plist
if $argc == 0
help plist
else
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
if $argc == 2
printf "elem[%u]: ", $size
p *($arg1*)($current + 1)
end
if $argc == 3
if $size == $arg2
printf "elem[%u]: ", $size
p *($arg1*)($current + 1)
end
end
set $current = $current._M_next
set $size++
end
printf "List size = %u \n", $size
if $argc == 1
printf "List "
whatis $arg0
printf "Use plist <variable_name> <element_type> to see the elements in the list.\n"
end
end
end document plist
Prints std::list<T> information.
Syntax: plist <list> <T> <idx>: Prints list size, if T defined all elements or just element at idx
Examples:
plist l - prints list size and definition
plist l int - prints all elements and list size
plist l int 2 - prints the third element in the list (if exists) and list size
end define plist_member
if $argc == 0
help plist_member
else
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
if $argc == 3
printf "elem[%u]: ", $size
p (*($arg1*)($current + 1)).$arg2
end
if $argc == 4
if $size == $arg3
printf "elem[%u]: ", $size
p (*($arg1*)($current + 1)).$arg2
end
end
set $current = $current._M_next
set $size++
end
printf "List size = %u \n", $size
if $argc == 1
printf "List "
whatis $arg0
printf "Use plist_member <variable_name> <element_type> <member> to see the elements in the list.\n"
end
end
end document plist_member
Prints std::list<T> information.
Syntax: plist <list> <T> <idx>: Prints list size, if T defined all elements or just element at idx
Examples:
plist_member l int member - prints all elements and list size
plist_member l int member 2 - prints the third element in the list (if exists) and list size
end
然后使用plist方法和plist_member 来获取成员的值,
plist this->m_listBuff
List size = 16595
其中引用计数为counter ,
counter =1 个数为204
counter = 0 个数为 16596
两者相加为16800,但是 list 里面,只有 16595 个元素,少掉的那个元素去哪了?没有进入链表唯一的可能是,链表中
一次__libc_message的排查的更多相关文章
- Tomcat shutdown执行后无法退出进程问题排查及解决
问题定位及排查 上周无意中调试程序在Linux上ps -ef|grep tomcat发现有许多tomcat的进程,当时因为没有影响系统运行就没当回事.而且我内心总觉得这可能是tomcat像nginx一 ...
- myrocks复制中断问题排查
背景 mysql可以支持多种不同的存储引擎,innodb由于其高效的读写性能,并且支持事务特性,使得它成为mysql存储引擎的代名词,使用非常广泛.随着SSD逐渐普及,硬件存储成本越来越高,面向写优化 ...
- Java线上应用故障排查之一:高CPU占用
一个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环. 以我们最近出现的一个实际故障为例,介绍怎么定位和解决这类问题. 根据top命令,发现PID为28555的Java进程占 ...
- wordpress插件bug排查后记(记一次由于开启memecached引起的插件bug)
这篇文章是写给自己的. 周三的时候我在维护公司的一个wordpress项目页面时发现了一个非常奇怪的情况:当我尝试更新网站上的一个页面后,在wordpress后台的编辑器中发现其内容并没有按我预期的将 ...
- [AlwaysOn Availability Groups]AG排查和监控指南
AG排查和监控指南 1. 排查场景 如下表包含了常用排查的场景.根据被分为几个场景类型,比如Configuration,client connectivity,failover和performance ...
- mysql半同步复制问题排查
1.问题背景 默认情况下,线上的mysql复制都是异步复制,因此在极端情况下,主备切换时,会有一定的概率备库比主库数据少,因此切换后,我们会通过工具进行回滚回补,确保数据不丢失.半同步复制则 ...
- 数据库实战案例—————记一次TempDB暴增的问题排查
前言 很多时候数据库的TempDB.日志等文件的暴增可能导致磁盘空间被占满,如果日常配置不到位,往往会导致数据库故障,业务被迫中断. 这种文件暴增很难排查,经验不足的一些运维人员可能更是无法排查具体原 ...
- 一次xbuild编译失败的排查
今天一个待上线服务测试完毕,需要构建CI,按照模板配置好包还原,xbuild编译,报错,错误信息如下: EtcdRegister.cs(8,15): error CS0234: The type or ...
- 一次kibana服务失败的排查过程
公司在kubernetes集群上稳定运行数月的kibana服务于昨天下午突然无法正常提供服务,访问kibana地址后提示如下信息: 排查过程: 看到提示后,第一反应肯定是检查elasticsearch ...
随机推荐
- 如何把kotlin+spring boot开发的项目部署在tomcat上
本文只讲部署过程,你首先要保证你的程序能在IDE里跑起来: 先看看你的application.properties中设置的端口号与你服务器上tomcat的端口号是否一致 server.port=80 ...
- bootstrap媒体查询
Bootstrap 中的媒体查询允许您基于视口大小移动.显示并隐藏内容.下面的媒体查询在 LESS 文件中使用,用来创建 Bootstrap 网格系统中的关键的分界点阈值. /* 超小设备(手机,小于 ...
- Java学习笔记16---抽象类与接口的浅显理解
抽象类是由abstract修饰的类,定义方式如public abstract class A{...}. 接口由interface修饰,定义方式如public interface B{...}. 抽象 ...
- Windows程序设计笔记(二) 关于编写简单窗口程序中的几点疑惑
在编写窗口程序时主要是5个步骤,创建窗口类.注册窗口类.创建窗口.显示窗口.消息环的编写.对于这5个步骤为何要这样写,当初我不是太理解,学习到现在有些问题我基本上已经找到了答案,同时对于Windows ...
- 8、ABPZero系列教程之拼多多卖家工具 添加手机注册登录功能
现在网站基本都用手机注册,很少用邮箱注册,本篇内容比较多,代码我会尽量加备注,有些操作需要连续添加几个文件才不报错,如果VS显示错误,请继续后续步骤. 前面已经有一篇文章讲到集成短信发送模块:http ...
- flask-日料网站搭建
引言:想使用python的flask框架搭建一个日料网站,主要包含web架构,静态页面,后台系统,交互. 本节知识:搭建web目录,目前正在copy网站. python环境:python2.7,fla ...
- 使用Linq确定序列是否包含任何元素
假设我们有一个集合,想要判断这个集合中是否包含任何元素可以使用Linq中的Any() List<string> list = new List<string> { " ...
- flask配置管理
配置管理 复杂的项目需要配置各种配置环境,如果设置项缺少可以直接硬编码进来,比如下面的方式: app.config['DEBUG']=True app.config['SECRET_KEY'] = ' ...
- java_web学习(三) eclipse_jsp学习
1.首先打开eclipse,新建一个Dynamac web project项目文件 2.在WebContent单击右键创建JSP File 3.过程 4.简单的jsp代码 运行结果: 5.导出war文 ...
- (亲测)躺着破解IDM下载权限,治疗不用破解补丁的强迫症们
首先.如果触犯了某些规则权限,请原谅. 很早以前就做过这个的破解,挺实用的,我今天就把之前写的经验贴出来大家一起学习学习~~~ 今天利用这个方法破解了最新版,最终的效果如下所示:我不是来刷存在感的.只 ...