@@ -5678,14 +5678,19 @@ is_dunder_name(PyObject *name)
5678
5678
static PyObject *
5679
5679
update_cache (struct type_cache_entry * entry , PyObject * name , unsigned int version_tag , PyObject * value )
5680
5680
{
5681
- _Py_atomic_store_uint32_relaxed (& entry -> version , version_tag );
5682
5681
_Py_atomic_store_ptr_relaxed (& entry -> value , value ); /* borrowed */
5683
5682
assert (_PyASCIIObject_CAST (name )-> hash != -1 );
5684
5683
OBJECT_STAT_INC_COND (type_cache_collisions , entry -> name != Py_None && entry -> name != name );
5685
5684
// We're releasing this under the lock for simplicity sake because it's always a
5686
5685
// exact unicode object or Py_None so it's safe to do so.
5687
5686
PyObject * old_name = entry -> name ;
5688
5687
_Py_atomic_store_ptr_relaxed (& entry -> name , Py_NewRef (name ));
5688
+ // We must write the version last to avoid _Py_TryXGetStackRef()
5689
+ // operating on an invalid (already deallocated) value inside
5690
+ // _PyType_LookupRefAndVersion(). If we write the version first then a
5691
+ // reader could pass the "entry_version == type_version" check but could
5692
+ // be using the old entry value.
5693
+ _Py_atomic_store_uint32_release (& entry -> version , version_tag );
5689
5694
return old_name ;
5690
5695
}
5691
5696
@@ -5762,7 +5767,7 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
5762
5767
// synchronize-with other writing threads by doing an acquire load on the sequence
5763
5768
while (1 ) {
5764
5769
uint32_t sequence = _PySeqLock_BeginRead (& entry -> sequence );
5765
- uint32_t entry_version = _Py_atomic_load_uint32_relaxed (& entry -> version );
5770
+ uint32_t entry_version = _Py_atomic_load_uint32_acquire (& entry -> version );
5766
5771
uint32_t type_version = _Py_atomic_load_uint32_acquire (& type -> tp_version_tag );
5767
5772
if (entry_version == type_version &&
5768
5773
_Py_atomic_load_ptr_relaxed (& entry -> name ) == name ) {
@@ -5809,11 +5814,14 @@ _PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef
5809
5814
int has_version = 0 ;
5810
5815
unsigned int assigned_version = 0 ;
5811
5816
BEGIN_TYPE_LOCK ();
5812
- res = find_name_in_mro (type , name , & error );
5817
+ // We must assign the version before doing the lookup. If
5818
+ // find_name_in_mro() blocks and releases the critical section
5819
+ // then the type version can change.
5813
5820
if (MCACHE_CACHEABLE_NAME (name )) {
5814
5821
has_version = assign_version_tag (interp , type );
5815
5822
assigned_version = type -> tp_version_tag ;
5816
5823
}
5824
+ res = find_name_in_mro (type , name , & error );
5817
5825
END_TYPE_LOCK ();
5818
5826
5819
5827
/* Only put NULL results into cache if there was no error. */
0 commit comments