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]

Reply via email to