Package: python-pyrex Version: 0.9.9-1 Severity: normal [crybaby /tmp/mdw]cat t.pyx ### -*-pyrex-*-
def test(x): try: return int(x) except TypeError: return 'failed' [crybaby /tmp/mdw]pyrexc t.pyx && gcc -fPIC -Og -g -c -I/usr/include/python2.7 t.c && ld -shared -otmodule.so t.o -lpython2.7 [crybaby /tmp/mdw]python -c"import t; t.test([])" zsh: segmentation fault python -c"import t; t.test([])" Oh, dear. What's happening? `t.c' is quite big, but here's what `test' compiled to. static PyObject *__pyx_f_1t_test(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) { PyObject *__pyx_v_x = 0; PyObject *__pyx_r; PyObject *__pyx_1 = 0; PyObject *__pyx_2 = 0; int __pyx_3; PyObject *__pyx_4 = 0; static char *__pyx_argnames[] = {"x",0}; if (!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "O", __pyx_argnames, &__pyx_v_x)) return 0; Py_INCREF(__pyx_v_x); /*try:*/ { __pyx_1 = PyTuple_New(1); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; goto __pyx_L2;} Py_INCREF(__pyx_v_x); PyTuple_SET_ITEM(__pyx_1, 0, __pyx_v_x); __pyx_2 = PyObject_CallObject(((PyObject *)(&PyInt_Type)), __pyx_1); if (!__pyx_2) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; goto __pyx_L2;} Py_DECREF(__pyx_1); __pyx_1 = 0; __pyx_r = __pyx_2; __pyx_2 = 0; goto __pyx_L0; } goto __pyx_L3; __pyx_L2:; Py_XDECREF(__pyx_1); __pyx_1 = 0; Py_XDECREF(__pyx_2); __pyx_2 = 0; /* "/tmp/mdw/t.pyx":5 */ __pyx_3 = PyErr_ExceptionMatches(PyExc_TypeError); if (__pyx_3) { Py_INCREF(__pyx_n_failed); __pyx_r = __pyx_n_failed; Py_DECREF(__pyx_1); __pyx_1 = 0; Py_DECREF(__pyx_2); __pyx_2 = 0; Py_DECREF(__pyx_4); __pyx_4 = 0; goto __pyx_L0; goto __pyx_L3; } goto __pyx_L1; __pyx_L3:; __pyx_r = Py_None; Py_INCREF(Py_None); goto __pyx_L0; __pyx_L1:; Py_XDECREF(__pyx_1); Py_XDECREF(__pyx_2); Py_XDECREF(__pyx_4); __Pyx_AddTraceback("t.test"); __pyx_r = 0; __pyx_L0:; Py_DECREF(__pyx_v_x); return __pyx_r; } Here, the `int' conversion is compiled as a generic function call, which is fair enough. If it fails, we `goto __pyx_L2', where we decrement the refcount of `__pyx_1' and then set the pointer to null. Then, if there was an exception matching `TypeError', we establish the return value and then /again/ try to decrement the refcount of `__pyx_1' -- only this time using `Py_DECREF' rather than `Py_XDECREF', which isn't tolerant of null pointers -- but we know at this point that `__pyx_1' is indeed null. Result: the segmentation fault we saw above. If I rewrite the code so as not to return from the `except' clause then the segfault disappears -- but there's another bug which I've reported separately (#875284). -- [mdw]