https://github.com/python/cpython/commit/c9380aebbe35973528392ff85f8914eb4c626d5b
commit: c9380aebbe35973528392ff85f8914eb4c626d5b
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-02-21T18:36:02+01:00
summary:
gh-141510: Check argument in PyDict_Contains() (#145083)
PyDict_Contains() and PyDict_ContainsString() now fail with
SystemError if the first argument is not a dict, frozendict, dict
subclass or frozendict subclass.
files:
M Lib/test/test_capi/test_dict.py
M Objects/dictobject.c
diff --git a/Lib/test/test_capi/test_dict.py b/Lib/test/test_capi/test_dict.py
index d3cc279cd3f955..f69ccbdbd1117d 100644
--- a/Lib/test/test_capi/test_dict.py
+++ b/Lib/test/test_capi/test_dict.py
@@ -223,6 +223,7 @@ def test_dict_getitemwitherror(self):
# CRASHES getitem(NULL, 'a')
def test_dict_contains(self):
+ # Test PyDict_Contains()
contains = _testlimitedcapi.dict_contains
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(contains(dct, 'a'))
@@ -235,11 +236,12 @@ def test_dict_contains(self):
self.assertRaises(TypeError, contains, {}, []) # unhashable
# CRASHES contains({}, NULL)
- # CRASHES contains(UserDict(), 'a')
- # CRASHES contains(42, 'a')
+ self.assertRaises(SystemError, contains, UserDict(), 'a')
+ self.assertRaises(SystemError, contains, 42, 'a')
# CRASHES contains(NULL, 'a')
def test_dict_contains_string(self):
+ # Test PyDict_ContainsString()
contains_string = _testcapi.dict_containsstring
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(contains_string(dct, b'a'))
@@ -251,6 +253,8 @@ def test_dict_contains_string(self):
self.assertTrue(contains_string(dct2, b'a'))
self.assertFalse(contains_string(dct2, b'b'))
+ self.assertRaises(SystemError, contains_string, UserDict(), 'a')
+ self.assertRaises(SystemError, contains_string, 42, 'a')
# CRASHES contains({}, NULL)
# CRASHES contains(NULL, b'a')
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 276e1df21a80d8..0a8ba74c2287c1 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -140,6 +140,7 @@ static PyObject* frozendict_new(PyTypeObject *type,
PyObject *args,
PyObject *kwds);
static PyObject* dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static int dict_merge(PyObject *a, PyObject *b, int override);
+static int dict_contains(PyObject *op, PyObject *key);
static int dict_merge_from_seq2(PyObject *d, PyObject *seq2, int override);
@@ -4126,7 +4127,7 @@ dict_merge(PyObject *a, PyObject *b, int override)
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
if (override != 1) {
- status = PyDict_Contains(a, key);
+ status = dict_contains(a, key);
if (status != 0) {
if (status > 0) {
if (override == 0) {
@@ -4484,7 +4485,7 @@ static PyObject *
dict___contains___impl(PyDictObject *self, PyObject *key)
/*[clinic end generated code: output=1b314e6da7687dae input=fe1cb42ad831e820]*/
{
- int contains = PyDict_Contains((PyObject *)self, key);
+ int contains = dict_contains((PyObject *)self, key);
if (contains < 0) {
return NULL;
}
@@ -4984,9 +4985,8 @@ static PyMethodDef mapp_methods[] = {
{NULL, NULL} /* sentinel */
};
-/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
-int
-PyDict_Contains(PyObject *op, PyObject *key)
+static int
+dict_contains(PyObject *op, PyObject *key)
{
Py_hash_t hash = _PyObject_HashFast(key);
if (hash == -1) {
@@ -4997,6 +4997,18 @@ PyDict_Contains(PyObject *op, PyObject *key)
return _PyDict_Contains_KnownHash(op, key, hash);
}
+/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
+int
+PyDict_Contains(PyObject *op, PyObject *key)
+{
+ if (!PyAnyDict_Check(op)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+
+ return dict_contains(op, key);
+}
+
int
PyDict_ContainsString(PyObject *op, const char *key)
{
@@ -5013,7 +5025,7 @@ PyDict_ContainsString(PyObject *op, const char *key)
int
_PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
{
- PyDictObject *mp = (PyDictObject *)op;
+ PyDictObject *mp = _PyAnyDict_CAST(op);
PyObject *value;
Py_ssize_t ix;
@@ -5042,7 +5054,7 @@ static PySequenceMethods dict_as_sequence = {
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- PyDict_Contains, /* sq_contains */
+ dict_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
@@ -6292,7 +6304,7 @@ dictkeys_contains(PyObject *self, PyObject *obj)
_PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL)
return 0;
- return PyDict_Contains((PyObject *)dv->dv_dict, obj);
+ return dict_contains((PyObject *)dv->dv_dict, obj);
}
static PySequenceMethods dictkeys_as_sequence = {
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]