一个C语言的惨痛教训

来源:互联网 发布:网络连接配置存在问题 编辑:程序博客网 时间:2024/04/29 09:53

一个c语言动态库开发的惨痛教训,作为警示,希望大家不要碰到这类问题。

背景

交代下背景,最近的一个项目,apache module开发,使用的是c语言。这个其实比较无奈,apache mod_dav.h中声明的dav_error结构体,其中一个字段就是const char *namespace,这不是鄙视C++吗?

虽然有方法可以绕过,搞成C++的,最后还是决定用c语言开发。

悲剧

悲剧就是这样发生的。

调试发现模块crash频繁,用gdb调试,结果让人迷惑,在pool上分配的地址竟然out of bound,诸如如下面的代码。

ptr = apr_psprintf(r->pool, "<lp%d:href>%s</lp%d:href>", ns, ptr, ns);

resp = apr_pcalloc(pool, sizeof(*resp));

resp->href = apr_pstrdup(pool, uri);

...

而且有时候程序crash时,发现函数的入栈参数明显有问题,参数错位。折腾了两天也没查出是什么原因。怀疑是pool被程序破坏了,但该pool上分配的其他地址都很正常,检查n+1编程序也没发现什么问题。搞的相当于郁闷。本来感到apr的内存分配系统还是挺不错的,不过现在想想,pool要是真的不小心被程序破坏了,那也是相当难以查出来的。

发现原因

按照pool破坏的思路折腾了几天,实在是没找到头绪。这个问题原因的发现还是听偶然的,就是仔细检查编译警告时,发现编译器对于apr_xxx的string系列操作函数发出警告:

 warning: assignment makes pointer from integer without a cast

我算是比较粗心的,对编译错误一般不仔细看,发现这个警告要归功于我的一位可爱的同事微笑

当时我看到这个警告就怀疑,是不是这个原因引起的crash。阴霍的天空无疑透出了一丝光亮。

相关.h文件没有包含导致编译器不认识这些库函数,如同编译warning所显示的那样:于是c语言就会把指针cast为int。由于编译机器是64位linux,如果把分配的地址空间cast到32位的int型数据,那么对于超过4G的地址空间来讲,这个结果就是一场灾难。

通常情况,如果函数没有定义,本来在运行时会报链接错误。但是这些是库函数,所以能够正常链接。

添加头文件,重新编译运行。程序不再crash。

十年被蛇咬。马上动手把所有的编译warning都修复掉。怕了...

教训

不要忽略编译warning

编译条件要严,像函数没有声明这种情况,应该设置为error,而非warning。我们的编译框架不够严格。

原创粉丝点击