@@ -5164,14 +5164,19 @@ is_dunder_name(PyObject *name)
5164
5164
static PyObject *
5165
5165
update_cache (struct type_cache_entry * entry , PyObject * name , unsigned int version_tag , PyObject * value )
5166
5166
{
5167
- _Py_atomic_store_uint32_relaxed (& entry -> version , version_tag );
5168
5167
_Py_atomic_store_ptr_relaxed (& entry -> value , value ); /* borrowed */
5169
5168
assert (_PyASCIIObject_CAST (name )-> hash != -1 );
5170
5169
OBJECT_STAT_INC_COND (type_cache_collisions , entry -> name != Py_None && entry -> name != name );
5171
5170
// We're releasing this under the lock for simplicity sake because it's always a
5172
5171
// exact unicode object or Py_None so it's safe to do so.
5173
5172
PyObject * old_name = entry -> name ;
5174
5173
_Py_atomic_store_ptr_relaxed (& entry -> name , Py_NewRef (name ));
5174
+ // We must write the version last to avoid _Py_TryXGetStackRef()
5175
+ // operating on an invalid (already deallocated) value inside
5176
+ // _PyType_LookupRefAndVersion(). If we write the version first then a
5177
+ // reader could pass the "entry_version == type_version" check but could
5178
+ // be using the old entry value.
5179
+ _Py_atomic_store_uint32_release (& entry -> version , version_tag );
5175
5180
return old_name ;
5176
5181
}
5177
5182
@@ -5235,7 +5240,7 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
5235
5240
// synchronize-with other writing threads by doing an acquire load on the sequence
5236
5241
while (1 ) {
5237
5242
uint32_t sequence = _PySeqLock_BeginRead (& entry -> sequence );
5238
- uint32_t entry_version = _Py_atomic_load_uint32_relaxed (& entry -> version );
5243
+ uint32_t entry_version = _Py_atomic_load_uint32_acquire (& entry -> version );
5239
5244
uint32_t type_version = _Py_atomic_load_uint32_acquire (& type -> tp_version_tag );
5240
5245
if (entry_version == type_version &&
5241
5246
_Py_atomic_load_ptr_relaxed (& entry -> name ) == name ) {
@@ -5281,11 +5286,14 @@ _PyType_LookupRef(PyTypeObject *type, PyObject *name)
5281
5286
int has_version = 0 ;
5282
5287
int version = 0 ;
5283
5288
BEGIN_TYPE_LOCK ();
5284
- res = find_name_in_mro (type , name , & error );
5289
+ // We must assign the version before doing the lookup. If
5290
+ // find_name_in_mro() blocks and releases the critical section
5291
+ // then the type version can change.
5285
5292
if (MCACHE_CACHEABLE_NAME (name )) {
5286
5293
has_version = assign_version_tag (interp , type );
5287
5294
version = type -> tp_version_tag ;
5288
5295
}
5296
+ res = find_name_in_mro (type , name , & error );
5289
5297
END_TYPE_LOCK ();
5290
5298
5291
5299
/* Only put NULL results into cache if there was no error. */
0 commit comments