Commit 77aa396b authored by Jeroen Demeyer's avatar Jeroen Demeyer Committed by Petr Viktorin

bpo-36907: fix refcount bug in _PyStack_UnpackDict() (GH-13381)

parent b892d3ea
...@@ -8,6 +8,7 @@ except ImportError: ...@@ -8,6 +8,7 @@ except ImportError:
import struct import struct
import collections import collections
import itertools import itertools
import gc
class FunctionCalls(unittest.TestCase): class FunctionCalls(unittest.TestCase):
...@@ -457,6 +458,22 @@ class FastCallTests(unittest.TestCase): ...@@ -457,6 +458,22 @@ class FastCallTests(unittest.TestCase):
result = _testcapi.pyobject_fastcallkeywords(func, args, kwnames) result = _testcapi.pyobject_fastcallkeywords(func, args, kwnames)
self.check_result(result, expected) self.check_result(result, expected)
def test_fastcall_clearing_dict(self):
# Test bpo-36907: the point of the test is just checking that this
# does not crash.
class IntWithDict:
__slots__ = ["kwargs"]
def __init__(self, **kwargs):
self.kwargs = kwargs
def __index__(self):
self.kwargs.clear()
gc.collect()
return 0
x = IntWithDict(dont_inherit=IntWithDict())
# We test the argument handling of "compile" here, the compilation
# itself is not relevant. When we pass flags=x below, x.__index__() is
# called, which changes the keywords dict.
compile("pass", "", "exec", x, **x.kwargs)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
Fix a crash when calling a C function with a keyword dict (``f(**kwargs)``)
and changing the dict ``kwargs`` while that function is running.
...@@ -544,10 +544,14 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self, ...@@ -544,10 +544,14 @@ _PyMethodDef_RawFastCallDict(PyMethodDef *method, PyObject *self,
} }
result = (*fastmeth) (self, stack, nargs, kwnames); result = (*fastmeth) (self, stack, nargs, kwnames);
if (stack != args) { if (kwnames != NULL) {
Py_ssize_t i, n = nargs + PyTuple_GET_SIZE(kwnames);
for (i = 0; i < n; i++) {
Py_DECREF(stack[i]);
}
PyMem_Free((PyObject **)stack); PyMem_Free((PyObject **)stack);
Py_DECREF(kwnames);
} }
Py_XDECREF(kwnames);
break; break;
} }
...@@ -1334,8 +1338,11 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, ...@@ -1334,8 +1338,11 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
return -1; return -1;
} }
/* Copy position arguments (borrowed references) */ /* Copy positional arguments */
memcpy(stack, args, nargs * sizeof(stack[0])); for (i = 0; i < nargs; i++) {
Py_INCREF(args[i]);
stack[i] = args[i];
}
kwstack = stack + nargs; kwstack = stack + nargs;
pos = i = 0; pos = i = 0;
...@@ -1344,8 +1351,8 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs, ...@@ -1344,8 +1351,8 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
called in the performance critical hot code. */ called in the performance critical hot code. */
while (PyDict_Next(kwargs, &pos, &key, &value)) { while (PyDict_Next(kwargs, &pos, &key, &value)) {
Py_INCREF(key); Py_INCREF(key);
Py_INCREF(value);
PyTuple_SET_ITEM(kwnames, i, key); PyTuple_SET_ITEM(kwnames, i, key);
/* The stack contains borrowed references */
kwstack[i] = value; kwstack[i] = value;
i++; i++;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment