Commit 5ebcdf8
committed
gh-132869: Fix crash due to memory ordering problem in dictobject under free threading.
Currently the reads and writes to a dictionary entry and to its contents are
unordered. For example in `do_lookup` we have the following code:
```
for (;;) {
ix = dictkeys_get_index(dk, i);
if (ix >= 0) {
int cmp = check_lookup(mp, dk, ep0, ix, key, hash);
```
where `dictkeys_get_index` performs a relaxed atomic read of `dk_indices[i]`,
where the `check_lookup` function might be, say, compare_unicode_unicode which
makes a relaxed load of the me_key value in that index.
```
PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
PyObject *ep_key = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key);
assert(ep_key != NULL);
```
However, the writer also does not order these two writes appropriately; for
example `insert_combined_dict` does the following:
```
Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
if (DK_IS_UNICODE(mp->ma_keys)) {
PyDictUnicodeEntry *ep;
ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
STORE_KEY(ep, key);
STORE_VALUE(ep, value);
}
else {
PyDictKeyEntry *ep;
ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
STORE_KEY(ep, key);
STORE_VALUE(ep, value);
STORE_HASH(ep, hash);
}
mp->ma_version_tag = new_version;
```
where the `dk_indices` value is set first, followed by setting the `me_key`,
both as relaxed writes. This is problematic because the write to
`dk_indices` may be ordered first, either by the program order on x86, or
allowed by the relaxed memory ordering semantics of ARM. The reader above
will be able to observe a state where the index has been set but the key value
is still null, leading to a crash in the reproducer of #132869.
The fix is two-fold:
* order the index write after the the write to its contents, and
* use sequentially consistent reads and writes. It would suffice to use
load-acquire and store-release here but those atomic operations do not exist
in the CPython atomic headers at the necessary types.
I was only able to reproduce the crash under CPython 3.13, but I do not
see any reason the bug is fixed on the 3.14 branch either since the code
does not seem to have changed.
Fixes #1328691 parent 8bb9286 commit 5ebcdf8
2 files changed
Lines changed: 8 additions & 7 deletions
File tree
- Misc/NEWS.d/next/Core_and_Builtins
- Objects
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
171 | | - | |
172 | | - | |
| 171 | + | |
| 172 | + | |
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| |||
1734 | 1734 | | |
1735 | 1735 | | |
1736 | 1736 | | |
1737 | | - | |
1738 | | - | |
1739 | 1737 | | |
1740 | 1738 | | |
1741 | 1739 | | |
| |||
1749 | 1747 | | |
1750 | 1748 | | |
1751 | 1749 | | |
| 1750 | + | |
1752 | 1751 | | |
1753 | 1752 | | |
1754 | 1753 | | |
| |||
1776 | 1775 | | |
1777 | 1776 | | |
1778 | 1777 | | |
1779 | | - | |
1780 | 1778 | | |
1781 | 1779 | | |
| 1780 | + | |
1782 | 1781 | | |
1783 | 1782 | | |
1784 | 1783 | | |
| |||
1906 | 1905 | | |
1907 | 1906 | | |
1908 | 1907 | | |
1909 | | - | |
1910 | 1908 | | |
1911 | 1909 | | |
1912 | 1910 | | |
| |||
1918 | 1916 | | |
1919 | 1917 | | |
1920 | 1918 | | |
| 1919 | + | |
1921 | 1920 | | |
1922 | 1921 | | |
1923 | 1922 | | |
| |||
2736 | 2735 | | |
2737 | 2736 | | |
2738 | 2737 | | |
2739 | | - | |
2740 | 2738 | | |
2741 | 2739 | | |
2742 | 2740 | | |
| |||
2750 | 2748 | | |
2751 | 2749 | | |
2752 | 2750 | | |
| 2751 | + | |
2753 | 2752 | | |
2754 | 2753 | | |
2755 | 2754 | | |
| |||
0 commit comments