Ticket #736 (closed defect: fixed)

Opened 20 months ago

Last modified 20 months ago

Crash when unpacking default arguments into closure variables

Reported by: scoder Owned by: scoder
Priority: critical Milestone: 0.15.1
Component: Code Generation Keywords:
Cc:

Description (last modified by scoder) (diff)

The following crashes in the argument unpacking code due to refcounting issues:

def default_args_for_closure(a=1, b=2):
    """
    >>> default_args_for_closure()()
    (1, 2)
    >>> default_args_for_closure(1, 2)()
    (1, 2)
    >>> default_args_for_closure(2)()
    (2, 2)
    >>> default_args_for_closure(8,9)()
    (8, 9)
    >>> default_args_for_closure(7, b=6)()
    (7, 6)
    >>> default_args_for_closure(a=5, b=4)()
    (5, 4)
    >>> default_args_for_closure(b=5, a=6)()
    (6, 5)
    """
    def func():
        return a,b
    return func

The generated C code is this:

  if (unlikely(__pyx_kwds)) {
    Py_ssize_t kw_args = PyDict_Size(__pyx_kwds);
    PyObject* values[2] = {0,0};
    values[0] = ((PyObject *)__pyx_int_1);
    values[1] = ((PyObject *)__pyx_int_2);
    switch (PyTuple_GET_SIZE(__pyx_args)) {
      case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
      case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      case  0: break;
      default: goto __pyx_L5_argtuple_error;
    }
    switch (PyTuple_GET_SIZE(__pyx_args)) {
      case  0:
      if (kw_args > 0) {
        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__a);
        if (value) { values[0] = value; kw_args--; }
      }
      case  1:
      if (kw_args > 0) {
        PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s__b);
        if (value) { values[1] = value; kw_args--; }
      }
    }
    if (unlikely(kw_args > 0)) {
      if (unlikely(__Pyx_ParseOptionalKeywords(/*...*/) /*...*/
    }
    __Pyx_INCREF(values[0]);
    __pyx_cur_scope->__pyx_v_a = values[0];
    __Pyx_INCREF(values[1]);
    __pyx_cur_scope->__pyx_v_b = values[1];
  } else {
    __pyx_cur_scope->__pyx_v_a = ((PyObject *)__pyx_int_1);
    __pyx_cur_scope->__pyx_v_b = ((PyObject *)__pyx_int_2);
    switch (PyTuple_GET_SIZE(__pyx_args)) {
      case  2: __Pyx_INCREF(PyTuple_GET_ITEM(__pyx_args, 1));
      __pyx_cur_scope->__pyx_v_b = PyTuple_GET_ITEM(__pyx_args, 1);
      case  1: __Pyx_INCREF(PyTuple_GET_ITEM(__pyx_args, 0));
      __pyx_cur_scope->__pyx_v_a = PyTuple_GET_ITEM(__pyx_args, 0);
      case  0: break;
      default: goto __pyx_L5_argtuple_error;
    }
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid(/*...*/);
  __pyx_L3_error:;
  __Pyx_AddTraceback(/*...*/);
  __Pyx_XDECREF(__pyx_cur_scope->__pyx_v_a); __pyx_cur_scope->__pyx_v_a = 0;
  __Pyx_XDECREF(__pyx_cur_scope->__pyx_v_b); __pyx_cur_scope->__pyx_v_b = 0;
  __Pyx_DECREF(((PyObject *)__pyx_cur_scope));
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_a);
  __Pyx_GIVEREF(__pyx_cur_scope->__pyx_v_b);

The optimised tuple-unpacking-only case at the end does not INCREF the default arguments.

Change History

Changed 20 months ago by scoder

  • description modified (diff)

Changed 20 months ago by scoder

  • priority changed from blocker to critical
  • status changed from new to closed
  • resolution set to fixed
Note: See TracTickets for help on using tickets.