2024-01-02

情况表现如下:

  1. debug 编译后程序崩溃。lldb run 后无法打印栈信息。

  2. c asan + debug 编译后 asan 报错:AddressSanitizer: unknown-crash on address 0x00016dd179a0 at pc 0x0001020e2fd8 bp 0x00016dd17290 sp 0x00016dd17288 报错地点在 BenchDataDeserialize。

    代码 crash 地点在 c 端。重复运行 crash 地点不变。

  3. rs asan + c asan 后相同错误。但是 crash 地点发生变化。

调试过程:

在 BenchDataDeserialize 中打断点,跳出函数后(在 take_next_sample)中发现 self 对象变为空指针。在调用 DataReader::take_next_sample 之前打断点,self 对象有效。

take_next_sample 中一个参数为指针。分别在传入指针之前打印指针的值和在 BenchDataDeserialize 中打印指针的值,发现两者不同。故认为参数传递过程中指针传递失败。

在 take_next_sample 中单步调试,发现传入的指针未被使用,传入的实际值是一个原地声明的无效值。

socket 占用 CPU 异常

现象:

  1. socket 在子线程中循环地去读取数据,其中套接字具备超时时间,当超时时,会打印一条 log。在运行时发现此线程占用 CPU 异常。

  2. 程序依赖于闭源模块,当此模块加载后,会导致上述问题,并影响模块加载之前创建的所有套接字,模块成功运行后再创建的套接字不受影响。已知闭源模块与受影响的套接字位于同一进程内。

  3. 对进程使用 strace 分析,可以看到模块加载后在尝试监听某地址,但是失败,此后以忙查询的形式不断创建套接字尝试监听地址。且可以看到 fd 不断增加,可以判断模块本身存在资源泄露情况。模块在失败后某个时间点会成功。之后创建的套接字不受影响。

由于 CPU 占用异常,因此猜测套接字本身被更改,有两种可能:

  • 套接字的超时时间被重置。

  • 套接字本身变成了非阻塞模式。

上述两种情况都会导致套接字的 recv 操作变成忙等状态,因而导致 CPU 状态异常。

套接字被修改可能是 socket 族函数被 HOOK,这一般发生在协程中,且 HOOK 一般以 preload 的形式加载,这与模块后加载,影响之前的套接字的现象相悖,尽管也 HOOK 也可以影响之前的函数,但是考虑到技术复杂度,可能性不大。

另一种可能是线程对 /proc/pid/fd 路径进行扫描,然后批量变更,但是考虑到正常人不会这么做,因此也摒弃。

根据第三点和 fd 本身是 int 类型可以判断,模块本身资源泄露,fd 本身不断上升,如果 fd 储存的类型发生了溢出(比如使用 char 储存 fd),就会导致模块操纵之前创建的套接字,当其将其它模块的套接字误认为自己的之后,就不会再更改其它的套接字了。这一分析与上述现象相符。

排查原因符合上述推论。

Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx