Skip to content

Commit bef5706

Browse files
vstinnernascheme
andauthored
gh-151593: Fix dead lock in PyDict insert_split_key() (#152200)
Do not hold LOCK_KEYS() lock when calling PyType_Modified() to avoid a deadlock. Co-authored-by: Neil Schemenauer <nas-github@arctrix.com>
1 parent 7c81637 commit bef5706

1 file changed

Lines changed: 18 additions & 1 deletion

File tree

Objects/dictobject.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,15 @@ set_values(PyDictObject *mp, PyDictValues *values)
214214
_Py_atomic_store_ptr_release(&mp->ma_values, values);
215215
}
216216

217+
// gh-151593: The _Py_LOCK_DONT_DETACH flag ensures that the outer critical
218+
// section is not dropped if there is some contention on the keys lock.
219+
// It also means that it will be important that LOCK_KEYS() is essentially the
220+
// "inner-most" code and that we don't call Py_DECREF() or similar while
221+
// holding the keys lock.
222+
//
223+
// We are not allowed to acquire other locks within LOCK_KEYS(). For example,
224+
// PyType_Modified() must not be called within LOCK_KEYS() since it acquires
225+
// the type lock.
217226
#define LOCK_KEYS(keys) PyMutex_LockFlags(&keys->dk_mutex, _Py_LOCK_DONT_DETACH)
218227
#define UNLOCK_KEYS(keys) PyMutex_Unlock(&keys->dk_mutex)
219228

@@ -1923,12 +1932,13 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
19231932
}
19241933
#endif
19251934

1935+
bool inserted = false;
19261936
LOCK_KEYS(keys);
19271937
ix = unicodekeys_lookup_unicode(keys, key, hash);
19281938
if (ix == DKIX_EMPTY && keys->dk_usable > 0) {
19291939
// Insert into new slot
1940+
inserted = true;
19301941
FT_ATOMIC_STORE_UINT32_RELAXED(keys->dk_version, 0);
1931-
_PyDict_SplitKeysInvalidated(keys);
19321942
Py_ssize_t hashpos = find_empty_slot(keys, hash);
19331943
ix = keys->dk_nentries;
19341944
dictkeys_set_index(keys, hashpos, ix);
@@ -1938,6 +1948,13 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
19381948
}
19391949
assert (ix < SHARED_KEYS_MAX_SIZE);
19401950
UNLOCK_KEYS(keys);
1951+
1952+
if (inserted) {
1953+
// gh-151593: Calling PyType_Modified() with LOCK_KEYS() creates a
1954+
// deadlock. So only call the function after UNLOCK_KEYS().
1955+
_PyDict_SplitKeysInvalidated(keys);
1956+
}
1957+
19411958
return ix;
19421959
}
19431960

0 commit comments

Comments
 (0)