分类 技术志 下的文章

遭遇Heap溢出

今天凌晨配合云南移动进行局数据全量升级,本来以为是件很轻松的活计,甚至不需要我动手的事情,结果却又是一次惨痛的教训啊。

这个活计其实真的很简单,就是将数据库中的旧数据全部删除,然后导入新的数据,由于数据量较大需要重启一次我们的系统。问题就在重启系统上。摆在我面前的就是"重启失败",系统dump一个core文件。通过pstack和gdb查看如下:
core 'core' of 7971: xxxxx -s
fe647b38 t_splay (3a71b0, 229, 228, 3a7000, 3ca548, 8000000) + 14
fe6475ec realfree (3ca320, 741f4, 320974, fe6bc000, 0, 3209a5) + c8
fe647e5c cleanfree (0, 7, fe6c29bc, 1a8, 3a7008, 0) + 54
fe646f88 _malloc_unlocked (ea60, 0, ff13de50, fe6bc000, ff184ae6, 0) + f4
fe646e78 malloc (ea60, 3e8, 0, 2, f8e9dacb, 1) + 20
000fa330 我们一业务函数,暂叫A_func吧 (18, 186a0, ffbfe4b0, 30330000, 37, ff00) + 1fc

碰到系统调用malloc出core,简单的初级的想法是:系统资源出现问题。使用df和top查看得知,物理内存居然有12G Free,而且出core的地方位于初始化阶段,系统大量使用内存的地方还未执行呢。

这个问题也真是碰巧了,A_func这个业务层接口恰恰是初始化我们今晨更新的局数据的接口,这又不由得让我去检验数据的有效性,经过回归数据,甚至是清空数据,问题依旧。

曾怀疑过内存对齐问题,但是自己明知道malloc出来的数据是经过编译器对齐的,经过测试后排除。

甚至怀疑这是Solaris的bug,在网上花了半个多小时搜索原因,未遂。每每当应用程序程序员遇到自己解决不了的问题时,都会归咎于操作系统。

在用户的多次催促下(大家知道电信的产品每年的宕机时间是有规定的),无奈下退回到以前的版本,发现居然可以启动。这说明什么呢?首先今晨更新的局数据是没有问题的,系统资源也是没有问题的,操作系统也是无恙的。一个新的思路急至眼前,比对一下新版和旧版功能上的差别,只有一个新增功能。恰好,这个功能(这里暂叫B_func)对应的初始化就在A_func之前调用的。快速转移到B_func内部实现当中,哇,问题
找到了。

问题源于Heap的溢出,原因当然是编码不当,马虎所致,更精确的说是:copy & paste所致。
在B_func中在原生Heap上动态申请了一块内存,内存大小为n * sizeof(A_Struct); 之后在B_func中对这块刚申请的内存进行了一次'清零'操作。就是这次'清零'操作惹下了大祸:memset(ptr_to_mem_alloced, 0, n * sizeof(B_Struct)); 而sizeof(B_Struct) > sizeof(A_Struct),这样大家就清楚了,由于'copy & paste',heap上的'组织网络'被局部的摧毁了。之后再使用heap上的数据单元自然逃离不了灭顶之灾。

这段代码是一个来到公司的新员工所写,这个员工是社招,水平不赖。其实真的不能埋怨这位员工,通过这次事件感觉责任最大的还是我,只怨我当初在评审代码时因此处简单而未加仔细评审,否则这种问题早就会被消灭在萌芽中了,也就不会出现今天的事件了。

这里也要感谢在远方的云南移动的兄弟顶住了投诉的压力,让我有时间找到问题所在啊。

以下是摘自WIKIPEDIA的关于"heap overflow"的描述:A heap overflow is another type of buffer overflow that occurs in the heap data area. Memory on the heap is dynamically allocated by the application at run-time and typically contains program data. Exploitation goes as follows: if an application copies data without first checking to see if it fits into the chunk (blocks of data in the heap), the attacker could supply the application with a piece of data that is too large, overwriting heap management information (metadata) of the next chunk. This allows an attacker to overwrite an arbitrary memory location with four bytes of data. In most environments, this may allow the attacker control over the program execution. 

在Solaris上编译Ethereal的注意事项

自从上次'编译Ethereal On Windows'之后,好久没有接触Ethereal了,前期策划的基于Ethereal开发的一个工具的任务就落到了这批来的一个新员工的头上了。第一阶段他在Windows上开发了一个基于Ethereal的插件用于分析CMPP协议之用;第二个阶段我们需要移植到Unix上,我们使用的是Solaris。

目标机器是一个x86的Solaris10的系统,首先是将Ethereal依赖的所有开源包都先装上。开源的唯一不好的一点就是互相依赖太多,像Ethereal这样规模的软件,依赖的包不下十几种。这里我们用的源码包是ethereal-0.99.0。按照常规编译软件包的方法:解包=>进入Ethereal目录=>执行./configure => make。

如果想一次make成功显然是不太可能的。

我的那个新同事第一次make得到如下结果:
libtool: link: only absolute run-paths are allowed,他查找了好久,终于"投降"了。由于是新同事对Unix上繁芜复杂的操作不了解也是情有可原的,亲自出马吧。

我对libtool同样是不熟悉,到网上找答案吧。网上关于这个错误的解释太少了。只能用"only absolute run-paths are allowed"在libtool这个脚本文件里搜索,果然找到了,有两个地方有这样的echo输出。大致检查了一下,基本定位问题所在:在libtool执行的语句中,有类似"-R../lib"这样的参数选项,显然这个"../lib"不是一个绝对路径,我们需要针对这个地方进行一下手工修改。

目标就是Makefile文件。打开Makefile搜寻"../lib",一共有4处,分别在SNMP_LIBS、ethereal_LDADD、tethereal_LDADD和dftest_LDADD的定义中,在其中删除"-R../lib"或者为-R指定一个绝对路径即可。

问题解决,继续Make。还是没有过去,提示:在"/usr/sfw/lib/.libs下找不到libnetsnmp.so",Makefile中明明配置的是/usr/sfw/lib,且该路径下存在libnetsnmp.so,为什么libtool非要到"/usr/sfw/lib/.libs"下找呢?libtool就是这样一个怪脾气,没办法,创建/usr/sfw/lib/.libs路径,并将libnetsnmp.so拷贝进去,在Make这块就过去了。

问题仍然层出不穷,程序在链接tethereal的时候,提示:ld: fatal: Symbol referencing errors. No output written to .libs/tethereal
Undefined                       first referenced
 symbol                             in file
gsm_a_pd_str                        tap-gsm_astat.o
RegistrationRejectReason_vals       tap-h225counter.o
register_all_protocol_handoffs      tethereal.o

继续在网上搜索,还好有类似的问题别人也遇到了,解决方法:将epan/dissectors/.libs/libdissectors.a加到Makefile中多个变量的定义中。下面是详细的修改:
(1)
tethereal_additional_libs = \
        wiretap/libwiretap.la           \
        epan/libethereal.la

=>

tethereal_additional_libs = \
        wiretap/libwiretap.la           \
        epan/libethereal.la \
        epan/dissectors/.libs/libdissectors.a \

(2)
dftest_additional_libs = \
        wiretap/libwiretap.la           \
        epan/libethereal.la \
=>
dftest_additional_libs = \
        wiretap/libwiretap.la           \
        epan/libethereal.la \
        epan/dissectors/.libs/libdissectors.a
(3)
dumpcap_LDADD = \
        $(dumpcap_additional_libs)      \
        -lgmodule-2.0 -lglib-2.0                        \
        -lpcap
=>
dumpcap_LDADD = \
        $(dumpcap_additional_libs)      \
        -lgmodule-2.0 -lglib-2.0                        \
        -lpcap -lsocket -lnsl
(4)
ethereal_additional_libs = \
        gtk/libui.a             \
        wiretap/libwiretap.la   \
        epan/libethereal.la \
=>
ethereal_additional_libs = \
        gtk/libui.a             \
        wiretap/libwiretap.la   \
        epan/libethereal.la \
        epan/dissectors/.libs/libdissectors.a \
这回你再make,哇,编译成功了。

然后敲入make install安装这个ethereal,在Windows上启动Xmanager(trial版),连接到目标服务器,用root登录(如果没有root权限,是看不到网卡的,也就不能进行协议分析了)。进入/usr/local/bin,在这里我只发现了tethereal这个程序,执行起来也没有问题,但是那个图形界面的ethereal哪里去了。翻看install的日志,发现根本没有安装ethereal。怎么回事?

继续网上找答案,很快答案找到了:是否编译安装ethereal是configure决定的。在configure的执行日志我看到:
The Ethereal package has been configured with the following options.
                    Build ethereal : no
                   Build tethereal : yes
                    Build capinfos : yes
                     Build editcap : yes
                     Build dumpcap : yes
                    Build mergecap : yes
                   Build text2pcap : yes
                     Build idl2eth : yes
                     Build randpkt : yes
                      Build dftest : yes

显然Build ethereal : no。网上的理由是:GTK+版本安装不当。从configure日志看到:
checking for GTK+ – version >= 2.0.0… no
*** Could not run GTK+ test program, checking why…
*** The test program failed to compile or link. See the file config.log for the
*** exact error that occured. This usually means GTK+ is incorrectly installed.

这时只能重新检查GTK+,甚至是重新安装GTK+。

搞定这个后,再重新configure,注意别忘了备份上述对Makefile的修改,否则configure会覆盖你的成果。之后的工作这里就不说了。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats