记录一场没有胜利的局部战斗
2015-05-14 00:00
1926 查看
这个问题, 早在1年前就遇到了, 当时因为没有在意一直没有跟进.
最近团队来了个新人, 又一次触发了这个问题, 所以终于下定决定要好好找找这个BUG的原因..
首先,这篇文章是个流水账, 没啥意思, 其次最终我其实也没能真正找到原因, 只是找到了一个规避的方法, 和一个猜测的原因. 万一遇到类似情况的同学可以部分借鉴, 当然, 最后我是希望有人对glibc源码熟悉的同学, 可以真正的指点下这个bug的真实原因是啥.
问题是这样的, 我们的一个服务, 依赖于Yar C库, 但是valgrind检测会报告一个问题:
好的吧, 到这里我首先放心了这不是我们代码的问题, 但是, 这肯定不符合我们打破沙锅问到底的精神啊, 那到底是我们的什么触发了这个Bug的警告呢? 我能不能找到原因, 从而规避这个问题呢, 因为我试过一些其他的库是不会有这个问题的.
首先我们来尝试找找看这个地址到底是什么东西, 通过一些精简, 我得到如下的一个可重现的方法, 首先main.c, 啥事都不干:
考虑到这个是__libc_freeres的问题(据称), 那么我们是不是可以直接调用从而浮现呢?
于是通过一个漫长痛苦的过程(这个过程按下不表), 终于得到了一个更加容易复现的lib库, 你猜他是什么样子呢?
好的, 现在让的main.c链接到这个新的库:
事情到此, 基本上绝对可以排除是我们自己的代码导致的, 那么到底会是什么原因呢?
没办法, 只好再回到valgrind(把main.c改回成最初的版本, 不要直接调用freeres), 本着试试的态度, db-attach上去:
那, 指定一个试试?
问题消失了….. 哈哈哈哈 (经历了从昨天下午, 昨天晚上, 到今天早上:
最近团队来了个新人, 又一次触发了这个问题, 所以终于下定决定要好好找找这个BUG的原因..
首先,这篇文章是个流水账, 没啥意思, 其次最终我其实也没能真正找到原因, 只是找到了一个规避的方法, 和一个猜测的原因. 万一遇到类似情况的同学可以部分借鉴, 当然, 最后我是希望有人对glibc源码熟悉的同学, 可以真正的指点下这个bug的真实原因是啥.
问题是这样的, 我们的一个服务, 依赖于Yar C库, 但是valgrind检测会报告一个问题:
==25279== Invalid free() / delete / delete[] / realloc()==25279== at 0x4A072BA: free (vg_replace_malloc.c:446)==25279== by 0x34DC10ADAA: free_mem (in /lib64/libc-2.5.so)==25279== by 0x34DC10A9A1: __libc_freeres (in /lib64/libc-2.5.so)==25279== by 0x48025E9: _vgnU_freeres (vg_preloaded.c:62)==25279== by 0x34DC0334E4: exit (in /lib64/libc-2.5.so)==25279== by 0x34DC01D99A: (below main) (in /lib64/libc-2.5.so)==25279== Address 0x4e265c8 is not stack'd, malloc'd or (recently) free'd当然首先遇到这个问题, 第一反映就是先Google了, Google以后很多答案指向说这个是低版本glibc(我们的是glibc-2.5)的libc_freeres的bug, 传递–run-libc-freeres=no 就可以避免这个警告. 通过验证发现确实管用.
好的吧, 到这里我首先放心了这不是我们代码的问题, 但是, 这肯定不符合我们打破沙锅问到底的精神啊, 那到底是我们的什么触发了这个Bug的警告呢? 我能不能找到原因, 从而规避这个问题呢, 因为我试过一些其他的库是不会有这个问题的.
首先我们来尝试找找看这个地址到底是什么东西, 通过一些精简, 我得到如下的一个可重现的方法, 首先main.c, 啥事都不干:
//file main.cint main(int argc, char **argv) { return 0;}然后编译, 链接yar库:
gcc -Wl,-rpath,/home/huixinchen/local/yar/lib/ -L/home/huixinchen/local/yar/lib/ -lyar main.c测试:
$ valgrind --run-libc-freeres=yes ./a.out==7008== Memcheck, a memory error detector==7008== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.==7008== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info==7008== Command: ./a.out==7008====7008== Invalid free() / delete / delete[] / realloc()==7008== at 0x4A072BA: free (vg_replace_malloc.c:446)==7008== by 0x34DC10ADAA: free_mem (in /lib64/libc-2.5.so)==7008== by 0x34DC10A9A1: __libc_freeres (in /lib64/libc-2.5.so)==7008== by 0x48025E9: _vgnU_freeres (vg_preloaded.c:62)==7008== by 0x34DC0334E4: exit (in /lib64/libc-2.5.so)==7008== by 0x34DC01D99A: (below main) (in /lib64/libc-2.5.so)==7008== Address 0x4e265c8 is not stack'd, malloc'd or (recently) free'd==7008==0x4e265c8在X86 64系统的内存布局结构中, 非常像一个全局变量的地址, 但是可能是什么呢? 会不会是.bss的一个全局变量呢? 通过sleep, 然后cat /proc/pid/maps, 发现valgrind会影响加载共享库的布局, 所以我们要想办法首先不用valgrind也能重现这个问题.
考虑到这个是__libc_freeres的问题(据称), 那么我们是不是可以直接调用从而浮现呢?
//file main.cextern void __libc_freeres();int main(int argc, char **argv) { __libc_freeres(); return 0;}测试运行:
$ ./a.out*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x00002ab9e8a21770 ***======= Backtrace: =========/lib64/libc.so.6(cfree+0x166)[0x34dc071756]/lib64/libc.so.6[0x34dc10adab]/lib64/libc.so.6(__libc_freeres+0x42)[0x34dc10a9a2]./a.out[0x4005d1]/lib64/libc.so.6(__libc_start_main+0xf4)[0x34dc01d994]./a.out[0x400509]果然可以直接触发, 不需要valgrind了, 但是yar库很庞大, 调试起来很复杂, 能不能进一步简化呢?
于是通过一个漫长痛苦的过程(这个过程按下不表), 终于得到了一个更加容易复现的lib库, 你猜他是什么样子呢?
#共享库是的, 你没看错, 这个库没有任何内容, 好的编译这个库(编译为libtest.so):
gcc -shared -fPIC empty.c -Wl,-rpath,/home/huixinchen/dev/ -lm -o libtest.so( 不要问, 为啥参数一定要有rpath和-lm, 这是因为这都是我在那个漫长痛苦的过程中总结出来的啊, 但如果你一定要问, 好吧, 这是因为Yar也有类似用法, 通过排除法以及Google试出来的…汗..)
好的, 现在让的main.c链接到这个新的库:
$ gcc -g -o mem-error main.c ./libtest.so$ ./mem-error*** glibc detected *** ./mem-error: free(): invalid pointer: 0x00002b1a30252660 ***======= Backtrace: =========/lib64/libc.so.6[0x34dc0711df]/lib64/libc.so.6(cfree+0x4b)[0x34dc07163b]/lib64/libc.so.6[0x34dc10adab]/lib64/libc.so.6(__libc_freeres+0x42)[0x34dc10a9a2]./mem-error(main+0xe)[0x4005c6]/lib64/libc.so.6(__libc_start_main+0xf4)[0x34dc01d994]./mem-error[0x400509]======= Memory map: ========00400000-00401000 r-xp 00000000 68:11 17932888 /home/huixinchen/dev/mem-error00600000-00601000 rw-p 00000000 68:11 17932888 /home/huixinchen/dev/mem-error04999000-049ba000 rw-p 04999000 00:00 0 [heap]304a600000-304a60d000 r-xp 00000000 68:01 950291 /lib64/libgcc_s-4.1.2-20080825.so.1304a60d000-304a80d000 ---p 0000d000 68:01 950291 /lib64/libgcc_s-4.1.2-20080825.so.1304a80d000-304a80e000 rw-p 0000d000 68:01 950291 /lib64/libgcc_s-4.1.2-20080825.so.131a6a00000-31a6a82000 r-xp 00000000 68:01 950274 /lib64/libm-2.5.so31a6a82000-31a6c81000 ---p 00082000 68:01 950274 /lib64/libm-2.5.so31a6c81000-31a6c82000 r--p 00081000 68:01 950274 /lib64/libm-2.5.so31a6c82000-31a6c83000 rw-p 00082000 68:01 950274 /lib64/libm-2.5.so34dbc00000-34dbc1c000 r-xp 00000000 68:01 950586 /lib64/ld-2.5.so34dbe1c000-34dbe1d000 r--p 0001c000 68:01 950586 /lib64/ld-2.5.so34dbe1d000-34dbe1e000 rw-p 0001d000 68:01 950586 /lib64/ld-2.5.so34dc000000-34dc14e000 r-xp 00000000 68:01 950587 /lib64/libc-2.5.so34dc14e000-34dc34d000 ---p 0014e000 68:01 950587 /lib64/libc-2.5.so34dc34d000-34dc351000 r--p 0014d000 68:01 950587 /lib64/libc-2.5.so34dc351000-34dc352000 rw-p 00151000 68:01 950587 /lib64/libc-2.5.so34dc352000-34dc357000 rw-p 34dc352000 00:00 02b1a30251000-2b1a30253000 rw-p 2b1a30251000 00:00 02b1a30253000-2b1a30254000 r-xp 00000000 68:11 17932574 /home/huixinchen/dev/libtest.so2b1a30254000-2b1a30453000 ---p 00001000 68:11 17932574 /home/huixinchen/dev/libtest.so2b1a30453000-2b1a30454000 rw-p 00000000 68:11 17932574 /home/huixinchen/dev/libtest.so好吧, 这样看起来这个地址(0x00002b1a30252660) 落在了libtest.so之前. 不会是我们的全局变量, 况且代码里压根就没有object对象…. (之前的地址确实是受到了valgrind的影响)
事情到此, 基本上绝对可以排除是我们自己的代码导致的, 那么到底会是什么原因呢?
没办法, 只好再回到valgrind(把main.c改回成最初的版本, 不要直接调用freeres), 本着试试的态度, db-attach上去:
0x0000000004a072ba in _vgr10050ZU_libcZdsoZa_free (p=0x4e265c8) at m_replacemalloc/vg_replace_malloc.c:446446 FREE(VG_Z_LIBC_SONAME, free, free );(gdb) bt#0 0x0000000004a072ba in _vgr10050ZU_libcZdsoZa_free (p=0x4e265c8) at m_replacemalloc/vg_replace_malloc.c:446#1 0x00000034dc10adab in free_mem () from /lib64/libc.so.6#2 0x00000034dc10a9a2 in __libc_freeres () from /lib64/libc.so.6#3 0x00000000048025ea in _vgnU_freeres () at vg_preloaded.c:62#4 0x00000034dc0334e5 in exit () from /lib64/libc.so.6#5 0x00000034dc01d99b in __libc_start_main () from /lib64/libc.so.6#6 0x00000000004004b9 in _start ()咦, 发现了什么不对的, 怎么是貌似在free SONAME呢? 我压根没有指定SONAME啊?
那, 指定一个试试?
$ gcc -shared -fPIC empty.c -Wl,-rpath,/home/huixinchen/dev/ -Wl,-soname,libm.so -lm -o libtest.so$ objdump -x libtest.so | grep SONAME SONAME libm.so然后, 你猜这么着?
问题消失了….. 哈哈哈哈 (经历了从昨天下午, 昨天晚上, 到今天早上:
Related Posts
PHP原理之内存管理中难懂的几个点相关文章推荐
- 升级日志——BMXAA4399E - 在当前记录中,没有可用的工作流操作——人员组
- SQL:显示每天的小计,某一天没有记录也要显示
- Win10运行记录不保存了,Win键+R运行没有历史记录解决方法
- dll 和 template(cegui中遇见的小问题,对解决任何问题没有任何帮助,仅记录)
- 不能读取记录;在MSysObjects上没有读取数据权限 access2007
- 初步进行泛微OA开发记录--在调休申请时判断是否加班没有加班时间的话不允许提交
- 百度面试题:有一个很大很大的输入流,大到没有存储器可以将其存储下来,而且只输入一次,如何从 这个输入 流中随机取得 m 个记录。
- 符合条件的记录有则修改没有则添加的小优化技巧
- Github没有记录Contributions的原因及解决方案
- 把遇到的问题记录成博客的习惯要找回来,不然又重走一边没有多大实际意义的路,。。。
- php rhythm当时没有记录日志更没理解php的yii
- 北理工java的试卷总结,我做了一下,发现也有许多以前没有理解的地方,现在再次记录下来
- 从此没有临时记录
- Xamarin.IOS问题记录——项目属性里IOS Bundle Signing 配置文件选项没有对应的配置文件选择
- 好久没有更新博客了,记录最近的C/S小项目
- 记录一次在 VirtualBox的添加共享windows文件后,发现没有共享文件的事
- 好久没有记录了
- 按天统计数据,如果当天没有记录,则统计为0
- 为什么Github没有记录你的Contributions
- 选出没有相同字段(goods字段)值的记录,并按照时间倒序排列