7

Python 调用动态库时 Segmentation fault (core dumped) 问题

 3 years ago
source link: https://yanbin.blog/python-call-shared-object-segmentation-fault-core-dumped/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python 调用动态库时 Segmentation fault (core dumped) 问题

2021-08-23 — Yanbin

这几天一直纠缠在如何调用动态库的问题上,先是 Go 语言,而后迁移到 Python 语言。在测试 Python 调用动态库时,出现过 "Segmentation fault (core dumped)" 的问题,本文记录下怎么去寻找线索,找到并解决问题的。

出现 "Segmentation fault (core dumped)" 的原因是多方面的,比如在 C/C++ 语言中

  1. 内存访问越界(数组越界,strcpy, strcat, sprintf, strcmp 等字符串函数读写越界)
  2. 多线程使用了线程不安全的函数
  3. 多线程读写的数据未加锁保护
  4. 非法指针(NULL 指针,随意的指针类型转换
  5. 堆栈溢出(如大的分配在栈上的局部变量)

用 Python 来调用动态库很大的可能性会是内存访问越界

下面来回顾并重现 "Segmentation fault (core dumped)" 这个问题,以 Linux 平台为例,首先在准备一个 C 动态库 testsf.c 文件,内容如

#include <stdio.h>
#include <string.h>
void foo (char* output)
    char* h = "hello";
    strncpy(output, h, 5);

用 gcc 编译得到动态库文件 libtestsf.so

$ gcc -fPIC -shared -o libtestsf.so testsf.c

试着写下面的 Python 调用代码 testsf.py

from ctypes import *
foo = cdll.LoadLibrary("./libtestsf.so").foo
foo.argtypes = (c_char_p,)
buf = c_char_p(10)
foo(buf)
print(buf.value.decode())

执行 python testsf.py

$ python testsf.py
Segmentation fault (core dumped)

没有更多的信息了,虽然提示说 core dumped,  但当前目录中没有发现 dumped 的 core 文件。原因是 ulimit 设置,默认时 ulimit -a 看到的

$ ulimit -a
core file size (blocks, -c) 0
......

core file size 为 0, 所以上面的 core dumped 是在撒谎,并没有生成 core 文件,我们可以用 ulimit -c unlimited(或设置一个具体数值) 打开 dump core 的选项

$ ulimit -c unlimited
$ ulimit -a
core file size (blocks, -c) unlimited

ulimit 是会话参数,所以重新连接终端后需要时又得重新执行 ulimit -c unlimited

这时再次执行 python testsf.py, 在当前目录中就会产生一个 core 文件

$ python testsf.py
Segmentation fault (core dumped)
$ ls -l core
-rw------- 1 vagrant vagrant 3235840 Aug 24 02:29 core

接下来要做的就是用 gdb 定位出问题的地方,没有 gdb 的用 yum 或 apt 自行安装

$ gdb python core           # gdb 执行程序(python) core文件

这时进到 gdb 的控制台,输入 bt, 就能看到哪里出问题了

python-so-sf-1-800x384.png

问题就出在对 strcpy 的函数调用上,越界了。原因是 c_char_p(10) 并非我们想要的 buffer, 它是不可访问的

aa = c_char_p(10)
print(aa.value)

上面的 Python 一执行立即会被非正常终止

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

我们应该使用 ctypes 的  create_string_buffer(size)  函数来创建一个缓冲,所以正确的 Python 使用前面动态库的代码如下

from ctypes import *
foo = cdll.LoadLibrary("./libtestsf.so").foo
foo.argtypes = (c_char_p,)
buf = create_string_buffer(10)
foo(buf)
print(buf.value.decode())

再执行 python testsf.py

$ python testsf.py
hello

一切正常,以后碰到这种  segment fault 的错误,就可以尝试着用 gdb 来寻求问题的解决办法。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK