Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b6a5340

Browse files
Issue #6083: Fix multiple segmentation faults occured when PyArg_ParseTuple
parses nested mutating sequence.
2 parents a4409c1 + 1d0bb9c commit b6a5340

7 files changed

Lines changed: 117 additions & 16 deletions

File tree

Lib/ctypes/test/test_returnfuncptrs.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,33 @@ def test_without_prototype(self):
3333
self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
3434
self.assertRaises(TypeError, strchr, b"abcdef")
3535

36+
def test_from_dll(self):
37+
dll = CDLL(_ctypes_test.__file__)
38+
# _CFuncPtr instances are now callable with a tuple argument
39+
# which denotes a function name and a dll:
40+
strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("strchr", dll))
41+
self.assertTrue(strchr(b"abcdef", b"b"), "bcdef")
42+
self.assertEqual(strchr(b"abcdef", b"x"), None)
43+
self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
44+
self.assertRaises(TypeError, strchr, b"abcdef")
45+
46+
# Issue 6083: Reference counting bug
47+
def test_test_from_dll_refcount(self):
48+
class BadSequence(tuple):
49+
def __getitem__(self, key):
50+
if key == 0:
51+
return "strchr"
52+
if key == 1:
53+
return CDLL(_ctypes_test.__file__)
54+
raise IndexError
55+
56+
# _CFuncPtr instances are now callable with a tuple argument
57+
# which denotes a function name and a dll:
58+
strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(BadSequence(("strchr", CDLL(_ctypes_test.__file__))))
59+
self.assertTrue(strchr(b"abcdef", b"b"), "bcdef")
60+
self.assertEqual(strchr(b"abcdef", b"x"), None)
61+
self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
62+
self.assertRaises(TypeError, strchr, b"abcdef")
63+
3664
if __name__ == "__main__":
3765
unittest.main()

Lib/test/test_functools.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,25 @@ def test_pickle(self):
194194
self.assertEqual(signature(f), signature(f_copy))
195195

196196
class TestPartialC(BaseTestC, TestPartial):
197-
pass
197+
198+
# Issue 6083: Reference counting bug
199+
def test_setstate_refcount(self):
200+
class BadSequence:
201+
def __len__(self):
202+
return 4
203+
def __getitem__(self, key):
204+
if key == 0:
205+
return max
206+
elif key == 1:
207+
return tuple(range(1000000))
208+
elif key in (2, 3):
209+
return {}
210+
raise IndexError
211+
212+
f = self.partial(object)
213+
self.assertRaisesRegex(SystemError,
214+
"new style getargs format but argument is not a tuple",
215+
f.__setstate__, BadSequence())
198216

199217
class TestPartialPy(BaseTestPy, TestPartial):
200218

@@ -204,7 +222,7 @@ def test_pickle(self):
204222
def test_repr(self):
205223
raise unittest.SkipTest("Python implementation of partial uses own repr")
206224

207-
class TestPartialCSubclass(BaseTestC, TestPartial):
225+
class TestPartialCSubclass(TestPartialC):
208226

209227
class PartialSubclass(c_functools.partial):
210228
pass

Lib/test/test_resource.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ def test_getrusage(self):
107107
except (ValueError, AttributeError):
108108
pass
109109

110+
# Issue 6083: Reference counting bug
111+
def test_setrusage_refcount(self):
112+
try:
113+
limits = resource.getrlimit(resource.RLIMIT_CPU)
114+
except AttributeError:
115+
pass
116+
else:
117+
class BadSequence:
118+
def __len__(self):
119+
return 2
120+
def __getitem__(self, key):
121+
if key in (0, 1):
122+
return len(tuple(range(1000000)))
123+
raise IndexError
124+
125+
resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
126+
110127
def test_main(verbose=None):
111128
support.run_unittest(ResourceTest)
112129

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ Core and Builtins
235235
Library
236236
-------
237237

238+
- Issue #6083: Fix multiple segmentation faults occured when PyArg_ParseTuple
239+
parses nested mutating sequence.
240+
238241
- Issue #5289: Fix ctypes.util.find_library on Solaris.
239242

240243
- Issue #17106: Fix a segmentation fault in io.TextIOWrapper when an underlying

Modules/_ctypes/_ctypes.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3188,23 +3188,37 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
31883188
{
31893189
char *name;
31903190
int (* address)(void);
3191+
PyObject *ftuple;
31913192
PyObject *dll;
31923193
PyObject *obj;
31933194
PyCFuncPtrObject *self;
31943195
void *handle;
31953196
PyObject *paramflags = NULL;
31963197

3197-
if (!PyArg_ParseTuple(args, "(O&O)|O", _get_name, &name, &dll, &paramflags))
3198+
if (!PyArg_ParseTuple(args, "O|O", &ftuple, &paramflags))
31983199
return NULL;
31993200
if (paramflags == Py_None)
32003201
paramflags = NULL;
32013202

3203+
ftuple = PySequence_Tuple(ftuple);
3204+
if (!ftuple)
3205+
/* Here ftuple is a borrowed reference */
3206+
return NULL;
3207+
3208+
if (!PyArg_ParseTuple(ftuple, "O&O", _get_name, &name, &dll)) {
3209+
Py_DECREF(ftuple);
3210+
return NULL;
3211+
}
3212+
32023213
obj = PyObject_GetAttrString(dll, "_handle");
3203-
if (!obj)
3214+
if (!obj) {
3215+
Py_DECREF(ftuple);
32043216
return NULL;
3217+
}
32053218
if (!PyLong_Check(obj)) {
32063219
PyErr_SetString(PyExc_TypeError,
32073220
"the _handle attribute of the second argument must be an integer");
3221+
Py_DECREF(ftuple);
32083222
Py_DECREF(obj);
32093223
return NULL;
32103224
}
@@ -3213,6 +3227,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
32133227
if (PyErr_Occurred()) {
32143228
PyErr_SetString(PyExc_ValueError,
32153229
"could not convert the _handle attribute to a pointer");
3230+
Py_DECREF(ftuple);
32163231
return NULL;
32173232
}
32183233

@@ -3227,6 +3242,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
32273242
PyErr_Format(PyExc_AttributeError,
32283243
"function ordinal %d not found",
32293244
(WORD)(size_t)name);
3245+
Py_DECREF(ftuple);
32303246
return NULL;
32313247
}
32323248
#else
@@ -3240,9 +3256,12 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
32403256
#else
32413257
PyErr_SetString(PyExc_AttributeError, ctypes_dlerror());
32423258
#endif
3259+
Py_DECREF(ftuple);
32433260
return NULL;
32443261
}
32453262
#endif
3263+
Py_INCREF(dll); /* for KeepRef */
3264+
Py_DECREF(ftuple);
32463265
if (!_validate_paramflags(type, paramflags))
32473266
return NULL;
32483267

@@ -3255,7 +3274,6 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
32553274

32563275
*(void **)self->b_ptr = address;
32573276

3258-
Py_INCREF((PyObject *)dll); /* for KeepRef */
32593277
if (-1 == KeepRef((CDataObject *)self, 0, dll)) {
32603278
Py_DECREF((PyObject *)self);
32613279
return NULL;

Modules/_functoolsmodule.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,10 @@ partial_reduce(partialobject *pto, PyObject *unused)
218218
}
219219

220220
static PyObject *
221-
partial_setstate(partialobject *pto, PyObject *args)
221+
partial_setstate(partialobject *pto, PyObject *state)
222222
{
223223
PyObject *fn, *fnargs, *kw, *dict;
224-
if (!PyArg_ParseTuple(args, "(OOOO):__setstate__",
224+
if (!PyArg_ParseTuple(state, "OOOO",
225225
&fn, &fnargs, &kw, &dict))
226226
return NULL;
227227
Py_XDECREF(pto->fn);
@@ -245,7 +245,7 @@ partial_setstate(partialobject *pto, PyObject *args)
245245

246246
static PyMethodDef partial_methods[] = {
247247
{"__reduce__", (PyCFunction)partial_reduce, METH_NOARGS},
248-
{"__setstate__", (PyCFunction)partial_setstate, METH_VARARGS},
248+
{"__setstate__", (PyCFunction)partial_setstate, METH_O},
249249
{NULL, NULL} /* sentinel */
250250
};
251251

Modules/resource.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,9 @@ resource_setrlimit(PyObject *self, PyObject *args)
142142
{
143143
struct rlimit rl;
144144
int resource;
145-
PyObject *curobj, *maxobj;
145+
PyObject *limits, *curobj, *maxobj;
146146

147-
if (!PyArg_ParseTuple(args, "i(OO):setrlimit",
148-
&resource, &curobj, &maxobj))
147+
if (!PyArg_ParseTuple(args, "iO:setrlimit", &resource, &limits))
149148
return NULL;
150149

151150
if (resource < 0 || resource >= RLIM_NLIMITS) {
@@ -154,21 +153,34 @@ resource_setrlimit(PyObject *self, PyObject *args)
154153
return NULL;
155154
}
156155

156+
limits = PySequence_Tuple(limits);
157+
if (!limits)
158+
/* Here limits is a borrowed reference */
159+
return NULL;
160+
161+
if (PyTuple_GET_SIZE(limits) != 2) {
162+
PyErr_SetString(PyExc_ValueError,
163+
"expected a tuple of 2 integers");
164+
goto error;
165+
}
166+
curobj = PyTuple_GET_ITEM(limits, 0);
167+
maxobj = PyTuple_GET_ITEM(limits, 1);
168+
157169
#if !defined(HAVE_LARGEFILE_SUPPORT)
158170
rl.rlim_cur = PyLong_AsLong(curobj);
159171
if (rl.rlim_cur == (rlim_t)-1 && PyErr_Occurred())
160-
return NULL;
172+
goto error;
161173
rl.rlim_max = PyLong_AsLong(maxobj);
162174
if (rl.rlim_max == (rlim_t)-1 && PyErr_Occurred())
163-
return NULL;
175+
goto error;
164176
#else
165177
/* The limits are probably bigger than a long */
166178
rl.rlim_cur = PyLong_AsLongLong(curobj);
167179
if (rl.rlim_cur == (rlim_t)-1 && PyErr_Occurred())
168-
return NULL;
180+
goto error;
169181
rl.rlim_max = PyLong_AsLongLong(maxobj);
170182
if (rl.rlim_max == (rlim_t)-1 && PyErr_Occurred())
171-
return NULL;
183+
goto error;
172184
#endif
173185

174186
rl.rlim_cur = rl.rlim_cur & RLIM_INFINITY;
@@ -182,10 +194,15 @@ resource_setrlimit(PyObject *self, PyObject *args)
182194
"not allowed to raise maximum limit");
183195
else
184196
PyErr_SetFromErrno(PyExc_OSError);
185-
return NULL;
197+
goto error;
186198
}
199+
Py_DECREF(limits);
187200
Py_INCREF(Py_None);
188201
return Py_None;
202+
203+
error:
204+
Py_DECREF(limits);
205+
return NULL;
189206
}
190207

191208
static PyObject *

0 commit comments

Comments
 (0)