Skip to content

Commit a5eb798

Browse files
committed
gh-146075: Prevent crash from malformed str subclass
In `partial_vectorcall`, an error returned by `PyDict_Contains` was considered to be a truthy value. Now, the error is handled appropriately.
1 parent e0f7c10 commit a5eb798

File tree

2 files changed

+17
-1
lines changed

2 files changed

+17
-1
lines changed

Lib/test/test_functools.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,19 @@ def __repr__(self):
565565
g_partial = functools.partial(func, trigger, None, None, None, None, arg=None)
566566
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None, None, None, None, arg=None)")
567567

568+
def test_str_subclass_error(self):
569+
class BadStr(str):
570+
def __eq__(self, other):
571+
raise RuntimeError
572+
def __hash__(self):
573+
return str.__hash__(self)
574+
575+
def f(**kwargs):
576+
return kwargs
568577

578+
p = functools.partial(f, poison="")
579+
with self.assertRaises(RuntimeError):
580+
result = p(**{BadStr("poison"): "new_value"})
569581

570582
@unittest.skipUnless(c_functools, 'requires the C _functools module')
571583
class TestPartialC(TestPartial, unittest.TestCase):

Modules/_functoolsmodule.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,11 @@ partial_vectorcall(PyObject *self, PyObject *const *args,
457457
for (Py_ssize_t i = 0; i < nkwds; ++i) {
458458
key = PyTuple_GET_ITEM(kwnames, i);
459459
val = args[nargs + i];
460-
if (PyDict_Contains(pto->kw, key)) {
460+
int contains = PyDict_Contains(pto->kw, key);
461+
if (contains < 0) {
462+
goto error;
463+
}
464+
else if (contains == 1) {
461465
if (pto_kw_merged == NULL) {
462466
pto_kw_merged = PyDict_Copy(pto->kw);
463467
if (pto_kw_merged == NULL) {

0 commit comments

Comments
 (0)