【发布时间】:2018-07-12 14:47:58
【问题描述】:
我一直在研究 C/C++ 中的 webrtc 数据通道库和 C 中的 wrote a program:
- 从同一进程创建两个对等点。
- 在它们之间建立连接。
- 如果连接成功,请关闭连接。
在 debian docker 容器和我的主机 opensuse tumbleweed(所有 x86_64 和 64 位)上一切正常,但在 alpine linux 容器(64 位 x86_64)上,我在子进程中获得了 SEGFAULT:
上面的函数来自程序的依赖“libnice”。似乎 *agent == NULL 并且在 caller's 范围内无法设为 null。我什至在函数调用之前插入了printf("Argument is %p", agent);,它打印出它的内存,我可以验证它不为空。从反汇编来看,复制代理的内容 (0x557a1d20) 作为被调用者堆栈中的局部变量会导致段错误。即使在make clean 和重新编译之后,段错误总是发生在这一点上。激活记录失败?堆栈损坏?
更新:我制作了一个更轻量级的容器并运行它,现在它在同一 priv_conn_keepalive_tick_unlocked 的不同位置出现了段错误。该参数似乎已设置(注意 0x7ffff7f9ad08):
因为我认为我可能会达到libmusl's 的默认堆栈限制 80k,所以我使用getrlimit(RLIMIT_STACK, &rl) 来获取堆栈大小,看起来它已经是 8 MB 而不是 80k。进一步增加这个限制似乎没有任何区别,除了如果我分配超过 8 MB,我的程序会提前在 Gdb 中崩溃。 Gdb 说它收到一个未知信号“??”;在 gdb 之外,它会在正常情况下崩溃,而不会更改堆栈大小。
我不确定到底是什么问题(堆栈损坏?)以及接下来要解决的问题。
这是我的程序流程:
对于创建的每个对等点,都会使用 fork() 创建一个子进程。父 子通信由 ZeroMQ 完成,我使用协议缓冲区将子进程内部触发的任何回调(及其参数)转发到父进程中运行的事件循环。
所以对于上面的程序,有2个子进程和1个父进程。
重现步骤:
- 源文件:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/examples/websocket_client/2in1.c
- 高山码头集装箱:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/Dockerfile.amd64
- 运行容器,二进制文件位于
/psl-librtcdcpp/examples/websocket_client/2in1 - 2in1 将生成两个子进程,这两个子进程都会崩溃。
【问题讨论】:
-
你有没有考虑过这个问题的根源在所有平台上都存在,只选择了一个出现?解决方案是大量调试并找到根本原因,在大多数平台上隐藏起来可能会变得更加困难。
-
假设你可以随意重现崩溃,我要做的第一件事就是插入一个 printf("calling priv_conn_keepalive_tick_unlocked with argument %p\n", agentPtr);紧接在调用该函数之前的行——这样你就可以验证调用是(或不是)使用 NULL 参数进行的。
-
此外,函数名称以后缀 _unlocked 结尾的事实让我怀疑函数的作者试图传达该函数不执行任何自己的互斥锁定,而是取决于在调用代码上锁定适当的互斥锁在调用函数之前。未能首先锁定必要的互斥体很可能会导致仅在某些情况下(例如仅在一个特定操作系统下)发生的崩溃,这与您的观察结果相符。
-
@JeremyFriesner 是的,我确实在函数调用之前添加了带有
agent参数的打印语句,并且指针不为空。 -
您尝试过哪些其他 Linux 版本?也不要认为你对正在发生的事情的分析是正确的。我怀疑您的堆栈损坏,并且您的局部变量已成为“垃圾”。
标签: c++ c linux alpine stack-corruption