免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 9702 | 回复: 47
打印 上一主题 下一主题

[C] 64bit OS 调用 隐式声明函数 的 返回值导致程序崩溃 [已解决] [复制链接]

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-03-24 02:41 |只看该作者 |倒序浏览
本帖最后由 robin10 于 2013-03-26 06:19 编辑

[1楼已经重新编辑。  为了减少干扰,把其他有干扰的信息也尽量去除]

原标题:
有 没有 可 能 是  malloc()  的 问题?

这是一个很狗血的BUG。
花费了3,4天的时间

原因是在64bit的系统上,隐式返回的指针默认是32bit的,当返回的指针值(应该其他数字也应该如此或者类似)大于32bit时,
系统会根据返回值的最高位补齐到64位,而补齐部分,将根据返回值的最高位不同而不同。
据说是这样的(以下内容为揣测,只为了说明道理,不代表正确,仅供参考):
1.如果最高位是0,则补0,即如果返回值为 0111 1111 1111 1111 1111 1111 1111 11111B, 则补齐后为 0x0000 0000 efff ffff;
2.如果最高位是1,则补1,即如果返回值为 1011 1111 1111 1111 1111 1111 1111 11111B, 则补齐后为 0xffff ffff  bfff ffff;

于是就有,当返回的指针值不大于32bit时, 程序正常运作;当返回值大于32bit时(并且最高位为1),系统补齐的高位为1,于是就得到了一个无效地址。。。

由于之前一直在ARM上工作,根本就不会遇到这种问题,对这方面一点概念都没有。

其实,这种隐式声明,在编译时 -Wall 参数会有警告。
按照习惯,所有的Warning最终自己都会清除。
只是在开始调试功能阶段,往往会先忽略这些warning.   这也是悲剧的根源!
而刚好,found_bakup_msg_node() 是前几天才添加的,还没有在头文件中声明,于是调用的时候变成了隐式声明调用的。    (不能偷懒啊。。。)

在头文件中添加对 found_bakup_msg_node()的声明,并在调用函数中包含该头文件以后,
目前运行良好,30次的测试,还没见到之前的SEG FAULT。 之前一般在测试5次左右就会出现一次。

希望这点教训,可以给自己和后来者一点好处!

再次谢谢所有给意见的XDJM



这方面更加详细的描述,请参考:
http://blogs.360.cn/360apps/2013 ... %E5%A3%B0%E6%98%8E/

http://bbs.chinaunix.net/thread-3755898-1-1.html


--------------------------------------------------------------------------------------------------------------------

代码的主要功能是
1. found_bakup_msg_node()  malloc一个 struct MSG_INFO 空间,然后把地址返回给调用函数。
2.调用found_bakup_msg_node() 的函数把内容拷贝到malloc得到的空间。

从LOG上来看,
在found_bakup_msg_node() 中打印的指针值 0x7f6090032f70,和调用该API的函数内部打印得到的指针值0xffffffff90032f70 不同(后8位相同)。
从值上看,应该是一个无效地址0xffffffff90032f70,(机器上8G空间)于是,往这个地址上写数据的时候,就出现了Segmentation fault.



------------------------------------ source code------------------------------------------------------------

{
.....
.....
        pMsg_next = header;
        while(pMsg_next != NULL) {

                pMsg_node = found_bakup_msg_node();
                if(pMsg_node != NULL) {
                        Dprintf("#%s#%d  %p\n", __FUNCTION__, __LINE__, pMsg_node);
                       
                        .....
                }
        }
       
        return ;
}

===================================================================

struct MSG_INFO* found_bakup_msg_node(void)
{
        struct MSG_INFO* msg_node = NULL;

        msg_node = (struct MSG_INFO*)malloc(sizeof(struct MSG_INFO));
        Dprintf("#%s#%d  %p\n", __FUNCTION__, __LINE__, msg_node);
        if(msg_node != NULL) {
                memset(msg_node, 0x0, sizeof(struct MSG_INFO));
//should use calloc()
        }

        return msg_node;
}


--------------------------------------------------LOG--------------------------------

#cp_msg_list#552 count = 0, header=0x923950
#found_bakup_msg_node#alloc_bakup_count=1 0x7f6090032f70 //malloc() 返回值
#cp_msg_list#561  0xffffffff90032f70  //接收到的指针

Segmentation fault
#
#

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
2 [报告]
发表于 2013-03-24 02:53 |只看该作者
FYI.

OS:
uname -a
Linux robin-desktop 2.6.32-38-generic #83-Ubuntu SMP Wed Jan 4 11:12:07 UTC 2012 x86_64 GNU/Linux

gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5.1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
3 [报告]
发表于 2013-03-24 03:03 |只看该作者
本帖最后由 robin10 于 2013-03-24 03:05 编辑

附正常运行的LOG。(代码没有更改)

#backup_msg_list#606
#cp_msg_list#544
#cp_msg_list#552 count = 0, header=0x1b32590
#found_bakup_msg_node#alloc_bakup_count=1 0x1bad630
#cp_msg_list#561
#cp_msg_node_data#458
#cp_msg_node_data#463 0x1bad630 0x1b32590
#cp_msg_node_data#470
#cp_msg_list#563
#cp_msg_list#566
#found_bakup_msg_node#alloc_bakup_count=2 0x1bad710
#cp_msg_list#561
#cp_msg_node_data#458
#cp_msg_node_data#463 0x1bad710 0x1b32950
#cp_msg_node_data#470
#cp_msg_list#563

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
4 [报告]
发表于 2013-03-24 05:47 |只看该作者
本帖最后由 robin10 于 2013-03-26 05:43 编辑

以下推测为错误

估计是多线程的malloc/free导致的。。。

1.以上列举的代码,包括之后的free()操作,都在锁内完成的。
2.程序使用到的几个库,其中2,3个都使用到了malloc/free,这些库编译的时候,并没有加 -pthread(这点是我的忽略)。
3.程序有创建线程,包含了"pthread.h".但是目前的makefile ×没有× 加 -pthread .
4.另外,程序使用了一个线程池库,这个库的编译有加 -pthread。

总的来说,程序拥有超过2条的线程,而不同的线程都可能malloc/free 内存。
而这个程序编译的时候并没有加 -pthread

所以。。这样应该是有问题的(?)

至于为什么malloc()返回的值比较奇怪。。可能已经是前面的原因导致的。。

根据上面理解,目前的测试可以相对容易的重复了seg fault.
而且并不一定在同一个地方CRASH 掉(起码从LOG来看是这样)。


论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
5 [报告]
发表于 2013-03-24 06:05 |只看该作者
目前还没有找到一个可以100%重现的方法。

如果上述理解有误,请指正。
谢谢。

论坛徽章:
0
6 [报告]
发表于 2013-03-24 12:45 |只看该作者
本帖最后由 txgc_wm 于 2013-03-24 12:46 编辑
robin10 发表于 2013-03-24 05:47
估计是多线程的malloc/free导致的。。。

1.以上列举的代码,包括之后的free()操作,都在锁内完成的。



1.以上列举的代码,包括之后的free()操作,都在锁内完成的。
=>我在mips平台上测试的时候,发现线程锁不能锁住的问题,不知pc上是否也可能存在该问题。不过当另一个线程将内存free掉的时候,我的程序不会出现段错误。以上的问题到现在还没有解决。
看楼主的调试信息(malloc的地址有误那块),是否是另外一条线程将同样的空间释放掉了,有“野指针”存在的问题?

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
7 [报告]
发表于 2013-03-24 15:18 |只看该作者
本帖最后由 robin10 于 2013-03-26 05:45 编辑
txgc_wm 发表于 2013-03-24 12:45
1.以上列举的代码,包括之后的free()操作,都在锁内完成的。
=>我在mips平台上测试的时候,发现线程 ...



1.从代码逻辑上来看,上面的malloc/free 的确是在锁内完成的,其他地方没有对该结构体进行malloc/free操作。
操作流程如下:

pthread_mutex_lock(&mutex_lock);

while(){
    info = malloc();
    memcpy(info, src, ..);//经常在此次挂掉
    add_node_to_list(info);
}

some_other_action();

while(list_is_not_null) {
    free(info);
}

pthread_mutex_unlock(&mutex_lock);


因此,不太可能出现double free 的情况(?),而且如果是double free,也应该有提示。

另外,以上操作,目前只会有一条线程会调用。假设这条线程是A。


pthread_mutex_lock()没有锁住 ?需要用 try_lock?


2.其他线程,而且不止一个线程,也有malloc/free操作,没有加锁。

3.其他线程中,假设是线程B,malloc的内容,会在 线程A 中 free.
而线程B只负责malloc,填充数据,但不会调用free.

4.所以,目前我怀疑的是,会否 线程B申请了某块内存,并往里面填充了数据,此时,线程A 也申请了同一块内存,
或者说,假设:
a> 线程B申请内存 0x00a0 ,长度为100B,
b> 线程A申请内存,可能地址是 0x00af,长度为 0x20,
c> 线程B填充内存,假设内容全部为 0xff
d> 而此时,线程A的长度就变成了 0xffff之类的?

但是,怎么解析A  malloc 返回的地址就已经不对呢?

4.程序使用的至少2个库函数内包含有malloc/free操作。这2个库编译时候没有-pthread.  
  昨晚看了一个别人的实验记录,称即使是编译时候加了 -pthread ,但是如果从不调用 pthread_create/init....类似的的函数的话,
  那么实际上并没有真正使用线程安全的malloc/free (不清楚是否这样?这个回头会在详细读一下)
  如果是的话,那么使用到的库,比如liba.so, 是否编译时候加了-pthread 也没实际意义?或者库的编译不需要-pthread 而只需要主程序添加-pthread就可?

这个我稍后会尝试。但是由于有时候比较难重现该现象,所以实验结果不一定就准确。
希望了解的兄弟指点一下。
谢谢

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
8 [报告]
发表于 2013-03-24 16:04 |只看该作者
特别地,
{
..
struct MSG*  pMsg_node  = NULL;
pMsg_next = header;
        while(pMsg_next != NULL) {

                pMsg_node = found_bakup_msg_node();
                if(pMsg_node != NULL) {
        Dprintf("#%s#%d\n", __FUNCTION__, __LINE__);
                        ret = cp_msg_node_data(pMsg_node, pMsg_next);
.....
}

pMsg 是函数内定义的,应该是可重进的。

1, pMsg 得到malloc 返回的地址是 0x7f6090032f70  //这个应该就已经不正常。
2. pMsg 传到下一个函数的时候,为什么变成了 0xffffffff90032f70 ?

论坛徽章:
0
9 [报告]
发表于 2013-03-24 16:10 |只看该作者
楼主,是否可以直接上code?  看楼主这样一步步的分析,大伙也了解不了多少,只能靠臆断。

论坛徽章:
1
程序设计版块每日发帖之星
日期:2016-06-04 06:20:00
10 [报告]
发表于 2013-03-24 16:22 |只看该作者
代码大概100K的CODE。。。目前并没有文档。。
我尽量的把关键部分提出来,把关联不大的忽略掉了。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP