diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2013-02-04 10:57:16 (GMT) |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2013-02-04 10:57:16 (GMT) |
commit | b6a53404b782c74bd9f7817ddb97f33642146d0c (patch) | |
tree | 79d7604ba115755b16ce1afe9c555c2f745482aa | |
parent | a4409c18eb55e3e76110af1ac88d8a8f6001d42f (diff) | |
parent | 1d0bb9c8f97b0f4fc6717f73555576f523965e42 (diff) | |
download | cpython-b6a53404b782c74bd9f7817ddb97f33642146d0c.zip cpython-b6a53404b782c74bd9f7817ddb97f33642146d0c.tar.gz cpython-b6a53404b782c74bd9f7817ddb97f33642146d0c.tar.bz2 |
Issue #6083: Fix multiple segmentation faults occured when PyArg_ParseTuple
parses nested mutating sequence.
-rw-r--r-- | Lib/ctypes/test/test_returnfuncptrs.py | 28 | ||||
-rw-r--r-- | Lib/test/test_functools.py | 22 | ||||
-rw-r--r-- | Lib/test/test_resource.py | 17 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_ctypes/_ctypes.c | 24 | ||||
-rw-r--r-- | Modules/_functoolsmodule.c | 6 | ||||
-rw-r--r-- | Modules/resource.c | 33 |
7 files changed, 117 insertions, 16 deletions
diff --git a/Lib/ctypes/test/test_returnfuncptrs.py b/Lib/ctypes/test/test_returnfuncptrs.py index af1cadd..21cb843 100644 --- a/Lib/ctypes/test/test_returnfuncptrs.py +++ b/Lib/ctypes/test/test_returnfuncptrs.py @@ -33,5 +33,33 @@ class ReturnFuncPtrTestCase(unittest.TestCase): self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) self.assertRaises(TypeError, strchr, b"abcdef") + def test_from_dll(self): + dll = CDLL(_ctypes_test.__file__) + # _CFuncPtr instances are now callable with a tuple argument + # which denotes a function name and a dll: + strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("strchr", dll)) + self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") + self.assertEqual(strchr(b"abcdef", b"x"), None) + self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) + self.assertRaises(TypeError, strchr, b"abcdef") + + # Issue 6083: Reference counting bug + def test_test_from_dll_refcount(self): + class BadSequence(tuple): + def __getitem__(self, key): + if key == 0: + return "strchr" + if key == 1: + return CDLL(_ctypes_test.__file__) + raise IndexError + + # _CFuncPtr instances are now callable with a tuple argument + # which denotes a function name and a dll: + strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(BadSequence(("strchr", CDLL(_ctypes_test.__file__)))) + self.assertTrue(strchr(b"abcdef", b"b"), "bcdef") + self.assertEqual(strchr(b"abcdef", b"x"), None) + self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0) + self.assertRaises(TypeError, strchr, b"abcdef") + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py index 828673c..30d7fb6 100644 --- a/Lib/test/test_functools.py +++ b/Lib/test/test_functools.py @@ -194,7 +194,25 @@ class TestPartial(object): self.assertEqual(signature(f), signature(f_copy)) class TestPartialC(BaseTestC, TestPartial): - pass + + # Issue 6083: Reference counting bug + def test_setstate_refcount(self): + class BadSequence: + def __len__(self): + return 4 + def __getitem__(self, key): + if key == 0: + return max + elif key == 1: + return tuple(range(1000000)) + elif key in (2, 3): + return {} + raise IndexError + + f = self.partial(object) + self.assertRaisesRegex(SystemError, + "new style getargs format but argument is not a tuple", + f.__setstate__, BadSequence()) class TestPartialPy(BaseTestPy, TestPartial): @@ -204,7 +222,7 @@ class TestPartialPy(BaseTestPy, TestPartial): def test_repr(self): raise unittest.SkipTest("Python implementation of partial uses own repr") -class TestPartialCSubclass(BaseTestC, TestPartial): +class TestPartialCSubclass(TestPartialC): class PartialSubclass(c_functools.partial): pass diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index 0240c69..bb3ff25 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -107,6 +107,23 @@ class ResourceTest(unittest.TestCase): except (ValueError, AttributeError): pass + # Issue 6083: Reference counting bug + def test_setrusage_refcount(self): + try: + limits = resource.getrlimit(resource.RLIMIT_CPU) + except AttributeError: + pass + else: + class BadSequence: + def __len__(self): + return 2 + def __getitem__(self, key): + if key in (0, 1): + return len(tuple(range(1000000))) + raise IndexError + + resource.setrlimit(resource.RLIMIT_CPU, BadSequence()) + def test_main(verbose=None): support.run_unittest(ResourceTest) @@ -235,6 +235,9 @@ Core and Builtins Library ------- +- Issue #6083: Fix multiple segmentation faults occured when PyArg_ParseTuple + parses nested mutating sequence. + - Issue #5289: Fix ctypes.util.find_library on Solaris. - Issue #17106: Fix a segmentation fault in io.TextIOWrapper when an underlying diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 838dd63..c195694 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3188,23 +3188,37 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) { char *name; int (* address)(void); + PyObject *ftuple; PyObject *dll; PyObject *obj; PyCFuncPtrObject *self; void *handle; PyObject *paramflags = NULL; - if (!PyArg_ParseTuple(args, "(O&O)|O", _get_name, &name, &dll, ¶mflags)) + if (!PyArg_ParseTuple(args, "O|O", &ftuple, ¶mflags)) return NULL; if (paramflags == Py_None) paramflags = NULL; + ftuple = PySequence_Tuple(ftuple); + if (!ftuple) + /* Here ftuple is a borrowed reference */ + return NULL; + + if (!PyArg_ParseTuple(ftuple, "O&O", _get_name, &name, &dll)) { + Py_DECREF(ftuple); + return NULL; + } + obj = PyObject_GetAttrString(dll, "_handle"); - if (!obj) + if (!obj) { + Py_DECREF(ftuple); return NULL; + } if (!PyLong_Check(obj)) { PyErr_SetString(PyExc_TypeError, "the _handle attribute of the second argument must be an integer"); + Py_DECREF(ftuple); Py_DECREF(obj); return NULL; } @@ -3213,6 +3227,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) if (PyErr_Occurred()) { PyErr_SetString(PyExc_ValueError, "could not convert the _handle attribute to a pointer"); + Py_DECREF(ftuple); return NULL; } @@ -3227,6 +3242,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) PyErr_Format(PyExc_AttributeError, "function ordinal %d not found", (WORD)(size_t)name); + Py_DECREF(ftuple); return NULL; } #else @@ -3240,9 +3256,12 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) #else PyErr_SetString(PyExc_AttributeError, ctypes_dlerror()); #endif + Py_DECREF(ftuple); return NULL; } #endif + Py_INCREF(dll); /* for KeepRef */ + Py_DECREF(ftuple); if (!_validate_paramflags(type, paramflags)) return NULL; @@ -3255,7 +3274,6 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds) *(void **)self->b_ptr = address; - Py_INCREF((PyObject *)dll); /* for KeepRef */ if (-1 == KeepRef((CDataObject *)self, 0, dll)) { Py_DECREF((PyObject *)self); return NULL; diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 3ff07bc..57dfba0 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -218,10 +218,10 @@ partial_reduce(partialobject *pto, PyObject *unused) } static PyObject * -partial_setstate(partialobject *pto, PyObject *args) +partial_setstate(partialobject *pto, PyObject *state) { PyObject *fn, *fnargs, *kw, *dict; - if (!PyArg_ParseTuple(args, "(OOOO):__setstate__", + if (!PyArg_ParseTuple(state, "OOOO", &fn, &fnargs, &kw, &dict)) return NULL; Py_XDECREF(pto->fn); @@ -245,7 +245,7 @@ partial_setstate(partialobject *pto, PyObject *args) static PyMethodDef partial_methods[] = { {"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS}, - {"__setstate__", (PyCFunction)partial_setstate, METH_VARARGS}, + {"__setstate__", (PyCFunction)partial_setstate, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/resource.c b/Modules/resource.c index 1aed497..b294a8c 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -142,10 +142,9 @@ resource_setrlimit(PyObject *self, PyObject *args) { struct rlimit rl; int resource; - PyObject *curobj, *maxobj; + PyObject *limits, *curobj, *maxobj; - if (!PyArg_ParseTuple(args, "i(OO):setrlimit", - &resource, &curobj, &maxobj)) + if (!PyArg_ParseTuple(args, "iO:setrlimit", &resource, &limits)) return NULL; if (resource < 0 || resource >= RLIM_NLIMITS) { @@ -154,21 +153,34 @@ resource_setrlimit(PyObject *self, PyObject *args) return NULL; } + limits = PySequence_Tuple(limits); + if (!limits) + /* Here limits is a borrowed reference */ + return NULL; + + if (PyTuple_GET_SIZE(limits) != 2) { + PyErr_SetString(PyExc_ValueError, + "expected a tuple of 2 integers"); + goto error; + } + curobj = PyTuple_GET_ITEM(limits, 0); + maxobj = PyTuple_GET_ITEM(limits, 1); + #if !defined(HAVE_LARGEFILE_SUPPORT) rl.rlim_cur = PyLong_AsLong(curobj); if (rl.rlim_cur == (rlim_t)-1 && PyErr_Occurred()) - return NULL; + goto error; rl.rlim_max = PyLong_AsLong(maxobj); if (rl.rlim_max == (rlim_t)-1 && PyErr_Occurred()) - return NULL; + goto error; #else /* The limits are probably bigger than a long */ rl.rlim_cur = PyLong_AsLongLong(curobj); if (rl.rlim_cur == (rlim_t)-1 && PyErr_Occurred()) - return NULL; + goto error; rl.rlim_max = PyLong_AsLongLong(maxobj); if (rl.rlim_max == (rlim_t)-1 && PyErr_Occurred()) - return NULL; + goto error; #endif rl.rlim_cur = rl.rlim_cur & RLIM_INFINITY; @@ -182,10 +194,15 @@ resource_setrlimit(PyObject *self, PyObject *args) "not allowed to raise maximum limit"); else PyErr_SetFromErrno(PyExc_OSError); - return NULL; + goto error; } + Py_DECREF(limits); Py_INCREF(Py_None); return Py_None; + + error: + Py_DECREF(limits); + return NULL; } static PyObject * |