pthread_detach()在64位Linux上导致SIGSEGV

时间:2014-01-24 09:11:48

标签: linux multithreading pthreads x86-64 sigsegv

以下是对我的情况的描述:我必须处理我们产品中的错误。该线程创建为joinable,它必须完成其工作,终止,没有人会为它调用pthread_join()。因此,线程是使用JOINABLE属性创建的(默认情况下),在终止之前它调用下一个代码:

{  pthread_detach(pthread_self()); pthread_exit(NULL); }

它就像我遇到的所有32位Linux发行版上的魅力一样,但它在64位发行版(Ubuntu 13.04 x86_64和Debian)上导致SIGSEGV。我没有尝试使用Slackware。这是一个核心:

Core was generated by `IsaVM -s=1 -PrjPath="/home/taf/Linux_Fov_540148/Cmds"  -stgMode=1 -PR -Failover'.
Program terminated with signal 11, Segmentation fault.
#0  0x00007f5911a7c009 in pthread_detach () from /lib/x86_64-linux-gnu/libpthread.so.0
(gdb) bt
#0  0x00007f5911a7c009 in pthread_detach () from /lib/x86_64-linux-gnu/libpthread.so.0
#1  0x000000000041310d in _kerCltDownloadThr (StartParams=0x6bfce0 <RESFOV>) at ./dker0clt.c:1258
#2  0x00007f5911a7ae9a in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0
#3  0x00007f591159f3fd in clone () from /lib/x86_64-linux-gnu/libc.so.6
#4  0x0000000000000000 in ?? ()

我想出了如何解决这个问题 - 我在创建线程之前为线程设置了CREATE_DETACHABLE属性(使用pthread_attr_setdetachstate())并且它按预期工作。

但是我的问题 - 调用此代码是犯罪吗?

{  pthread_detach(pthread_self()); pthread_exit(NULL); }

pthread_detach()在调用后异步执行某些操作会导致pthread_exit()带来问题吗?但崩溃点是pthread_detach()而不是pthread_exit()!我完全不明白这次撞车的原因!为什么它适用于32位?它是pthread实施中某处的竞争条件吗?

pthread_join()没有要求这个帖子。

提前感谢任何想法。

3 个答案:

答案 0 :(得分:1)

线程分离本身感觉不对。调用pthread_create()的线程通常负责,如果需要,可以创建一个分离的线程。

可能是线程已被分离。因为尝试分离已经分离的线程会导致未指定的行为。

我的顶级猜测是:

  1. 线程多次分离。作为快速检查,我会尝试在pthread_detach中的gdb上设置断点,以查看是否在此函数中传递了重复的线程ID。如果难以在gdb下运行您的应用程序,则另一个选项是覆盖pthread_createpthread_detach并跟踪线程ID以检测双重分离。请参阅http://hackerboss.com/overriding-system-functions-for-fun-and-profit/

  2. 内存损坏。如果可以在其下运行您的应用程序,valgrind可以帮助您检测内存损坏。或者,如果您使用-fstack-protector-all,请使用-fsanitize=address-fsanitize=threadgcc进行编译,尝试使用运行时错误检查来检测应用程序。 clang编译器还有一系列选项来检测此类错误,请参阅http://clang.llvm.org/docs/index.html上的清理程序。

答案 1 :(得分:0)

我用一种受人尊敬的@MaximYegorushkin提供的方法完成了我的研究。 AddressSanitizer向我展示了我们产品中的一个缓冲区obverflow,但它与我的问题没有关系(我肯定会在以后修复它,拥有这样一个明智的工具来捕获错误总是好的)。因此决定使用pthread_xxx方法覆盖所有必需的LD_PRELOAD函数。我运行一个简单的测试,以确保我的库按预期工作:

[HACK] Loading pthread hack.
Starting thread...!
[HACK] pthread_create: thread=7FAC6C86D700
Waiting for 2 seconds...
[HACK] pthread_self: thread=7FAC6C86D700
thread_func: thread id = 7FAC6C86D700
Thread: sin(3.26) = -0.121109
[HACK] pthread_self: thread=7FAC6C86D700
[HACK] pthread_detach: thread=7FAC6C86D700
Terminating...

[HACK] 开始的所有字符串都是由我的threadhack.so库生成的。 然后我用这个库运行我的项目它确切地指出了问题所在:

执行的代码:{ pthread_detach(pthread_self()); pthread_exit(NULL); }

调试跟踪:

[HACK] pthread_create: thread=7F403251CB00
.....
[HACK] pthread_self: thread=7F403251CB00  
[HACK] pthread_detach: thread=3251CB00    

所以我们看到pthread_self返回一个好的线程ID,但pthread_detach收到它已经损坏了(切成32位)。怎么会这样?我为我的简单工作测试应用程序生成了汇编代码作为参考和我的项目:

参考申请

call    pthread_self
movq    %rax, %rdi
call    pthread_detach
movl    $0, %edi
call    pthread_exit

所以我们在这里看到movq指令用于复制64位线程id(movq %rax, %rdi)。好的,检查GCC为我的项目生成的

movl    $0, %eax
call    pthread_self
movl    %eax, %edi
movl    $0, %eax
call    pthread_detach
movl    $0, %edi
movl    $0, %eax
call    pthread_exit

吁!我们有两个movl指令(32位),一个复制最低有效32位(movl %eax, %edi),而不是最重要的部分,它总是置零! (movl $0, %eax)。所以这是一个错误的thead id的原因。我不知道为什么代码如此不同 - 编译标志是相同的。我在GCC 4.7中看到了这个错误。我在GCC 4.8中看到了这个错误(来自Ubuntu 13.10 x86_64的最新包)。

所以至少现在我看到了什么是hapenning。感谢@Maxim和出色的工具。我又学到了一件新东西。

P.S。我不知道如何向GCC团队提交错误报告。我无法在一个简单的小应用程序上重现这个问题,我无法将它们交给我的项目,因为它是一个专有软件,而且我不会分发它。

答案 2 :(得分:0)

我的猜测是你在调用pthread_detach(pthread_self())的代码中没有pthread_detach或pthread_self的原型;如果没有原型,编译器将假定参数为int(pthread_detach)或函数返回int(pthread_self)。

虽然进一步思考,但我更怀疑pthread_self是未定义(返回int)或错误地定义为返回int的罪魁祸首。然后,编译器通过添加前导32位零来正确地将其扩展为64位整数。