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]