为什么这个 Py_DECREF 会导致段错误?

Why does this Py_DECREF cause a segfault?

我正在处理 Python 扩展中的一个烦人的段错误。深入到核心,我首先创建了一个独立的 C 版本的扩展,在尝试进一步减少问题的同时,我最终得到了这个。这是完整的程序:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int main(void) {
    PyObject *num;
    PyObject *args;
    num = PyLong_FromLong(0);
    if (!num) return -1;
    args = PyTuple_Pack(1, num);
    if (!args) return -1;
    Py_DECREF(args); /* <-- segfault */
    return 0;
}

如果我省略 Py_DECREF,我不会收到错误。根据文档,PyTuple_Pack returns 一个新参考,我现在 "own"。难道我不应该被允许对其进行 DECREF 吗?

运行在valgrind中,相关报错信息是这样的:

==7160== Memcheck, a memory error detector
==7160== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==7160== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==7160== Command: ./pyctest
==7160==
==7160== Invalid read of size 4
==7160==    at 0x4F2B13E: ??? (in /usr/lib64/libpython3.6m.so.1.0)
==7160==    by 0x40078B: main (pyctest.c:11)
==7160==  Address 0xa0 is not stack'd, malloc'd or (recently) free'd
==7160==
==7160==
==7160== Process terminating with default action of signal 11 (SIGSEGV)
==7160==  Access not within mapped region at address 0xA0
==7160==    at 0x4F2B13E: ??? (in /usr/lib64/libpython3.6m.so.1.0)
==7160==    by 0x40078B: main (pyctest.c:11)

编辑

一些构建细节。这是在 RHEL7 Linux 系统上。

$ python3-config --cflags
-I/usr/include/python3.6m -I/usr/include/python3.6m  -Wno-unused-result -Wsign-compare -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv   -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv
$ python3-config --ldflags
 -L/usr/lib64 -lpython3.6m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic
$ cat Makefile
LDFLAGS=-L/usr/lib64 -lpython3.6m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic
CFLAGS=-I/usr/include/python3.6m -I/usr/include/python3.6m  -Wno-unused-result -Wsign-compare -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv   -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches   -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv


pyctest: pyctest.c
        gcc $(CFLAGS) pyctest.c -o pyctest $(LDFLAGS)
$

段错误的发生可能是由于 Python 调试和生产环境之间的混淆导致的未定义行为。