Skip to content

Commit

Permalink
[3.13] gh-117657: Fix TSAN races in setobject.c (GH-121511) (#121541)
Browse files Browse the repository at this point in the history
The `used` field must be written using atomic stores because `set_len`
and iterators may access the field concurrently without holding the
per-object lock.
(cherry picked from commit 9c08f40)

Co-authored-by: Sam Gross <[email protected]>
  • Loading branch information
miss-islington and colesbury authored Jul 9, 2024
1 parent 8d473d8 commit f0d16f7
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 11 deletions.
18 changes: 10 additions & 8 deletions Objects/setobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,14 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
found_unused_or_dummy:
if (freeslot == NULL)
goto found_unused;
so->used++;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
freeslot->key = key;
freeslot->hash = hash;
return 0;

found_unused:
so->fill++;
so->used++;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
entry->key = key;
entry->hash = hash;
if ((size_t)so->fill*5 < mask*3)
Expand Down Expand Up @@ -357,7 +357,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
old_key = entry->key;
entry->key = dummy;
entry->hash = -1;
so->used--;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
Py_DECREF(old_key);
return DISCARD_FOUND;
}
Expand Down Expand Up @@ -397,7 +397,7 @@ set_empty_to_minsize(PySetObject *so)
{
memset(so->smalltable, 0, sizeof(so->smalltable));
so->fill = 0;
so->used = 0;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, 0);
so->mask = PySet_MINSIZE - 1;
so->table = so->smalltable;
so->hash = -1;
Expand Down Expand Up @@ -615,7 +615,7 @@ set_merge_lock_held(PySetObject *so, PyObject *otherset)
}
}
so->fill = other->fill;
so->used = other->used;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
return 0;
}

Expand All @@ -624,7 +624,7 @@ set_merge_lock_held(PySetObject *so, PyObject *otherset)
setentry *newtable = so->table;
size_t newmask = (size_t)so->mask;
so->fill = other->used;
so->used = other->used;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
for (i = other->mask + 1; i > 0 ; i--, other_entry++) {
key = other_entry->key;
if (key != NULL && key != dummy) {
Expand Down Expand Up @@ -678,7 +678,7 @@ set_pop_impl(PySetObject *so)
key = entry->key;
entry->key = dummy;
entry->hash = -1;
so->used--;
FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
so->finger = entry - so->table + 1; /* next place to start */
return key;
}
Expand Down Expand Up @@ -1173,7 +1173,9 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
Py_hash_t h;

t = a->fill; a->fill = b->fill; b->fill = t;
t = a->used; a->used = b->used; b->used = t;
t = a->used;
FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used);
FT_ATOMIC_STORE_SSIZE_RELAXED(b->used, t);
t = a->mask; a->mask = b->mask; b->mask = t;

u = a->table;
Expand Down
3 changes: 0 additions & 3 deletions Tools/tsan/suppressions_free_threading.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ race_top:assign_version_tag
race_top:insertdict
race_top:lookup_tp_dict
race_top:new_reference
# https://gist.github.com/colesbury/d13d033f413b4ad07929d044bed86c35
race_top:set_discard_entry
race_top:_PyDict_CheckConsistency
race_top:_Py_dict_lookup_threadsafe
race_top:_multiprocessing_SemLock_acquire_impl
Expand All @@ -41,7 +39,6 @@ race_top:insert_to_emptydict
race_top:insertdict
race_top:list_get_item_ref
race_top:make_pending_calls
race_top:set_add_entry
race_top:_Py_slot_tp_getattr_hook
race_top:add_threadstate
race_top:dump_traceback
Expand Down

0 comments on commit f0d16f7

Please sign in to comment.