GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Data race on `block->next` in `mi_block_set_nextx` · Issue #129748 · python/cpython

Data race on block->next in mi_block_set_nextx #129748

New issue
Copy link
New issue
Copy link
Closed
Closed
Data race on block->next in mi_block_set_nextx#129748
Copy link
Assignees
corona10
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@colesbury
colesbury
opened on Feb 6, 2025
Issue body actions

Bug report

I've seen this in non-debug TSan builds. The TSAN report looks like:

  Write of size 8 at 0x7fffc4043690 by thread T2692:
    #0 mi_block_set_nextx /raid/sgross/cpython/./Include/internal/mimalloc/mimalloc/internal.h:652:15 (python+0x2ce71e) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
    #1 _mi_free_block_mt /raid/sgross/cpython/Objects/mimalloc/alloc.c:467:9 (python+0x2ce71e)
    #2 _mi_free_block /raid/sgross/cpython/Objects/mimalloc/alloc.c:506:5 (python+0x2a8b9a) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
    #3 _mi_free_generic /raid/sgross/cpython/Objects/mimalloc/alloc.c:524:3 (python+0x2a8b9a)
    #4 mi_free /raid/sgross/cpython/Objects/mimalloc/alloc.c (python+0x2c765b) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
    #5 _PyObject_MiFree /raid/sgross/cpython/Objects/obmalloc.c:284:5 (python+0x2c765b)
...

  Previous atomic read of size 8 at 0x7fffc4043690 by thread T2690:
    #0 _Py_atomic_load_uintptr_relaxed /raid/sgross/cpython/./Include/cpython/pyatomic_gcc.h:375:10 (python+0x4d0341) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
    #1 _Py_IsOwnedByCurrentThread /raid/sgross/cpython/./Include/object.h:252:12 (python+0x4d0341)
    #2 _Py_TryIncrefFast /raid/sgross/cpython/./Include/internal/pycore_object.h:560:9 (python+0x4d0341)
    #3 _Py_TryIncrefCompare /raid/sgross/cpython/./Include/internal/pycore_object.h:599:9 (python+0x4d0341)
    #4 PyMember_GetOne /raid/sgross/cpython/Python/structmember.c:99:18 (python+0x4d0054) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
    #5 member_get /raid/sgross/cpython/Objects/descrobject.c:179:12 (python+0x2056aa) (BuildId: 2d15b5a5260b454c4f23bd5e53d32d43bfb806c4)
...

SUMMARY: ThreadSanitizer: data race /raid/sgross/cpython/./Include/internal/mimalloc/mimalloc/internal.h:652:15 in mi_block_set_nextx

This happens when we call _Py_TryIncrefCompare() or _Py_TryXGetRef or similar on an object that may be concurrently freed. Perhaps surprisingly, this is a supported operation. See https://peps.python.org/pep-0703/#mimalloc-changes-for-optimistic-list-and-dict-access.

The problem is mi_block_set_nextx doesn't use a relaxed store, so this is a data race because the mimalloc freelist pointer may overlap the ob_tid field. The mimalloc freelist pointer is at the beginning of the freed memory block and ob_tid is the first field in PyObject.

You won't see this data race if:

  • The object uses Py_TPFLAGS_MANAGED_DICT. In that case the beginning the managed dictionary pointer comes before ob_tid. That is fine because, unlike ob_tid, the managed dictionary pointer is never accessed concurrently with freeing the object.
  • If CPython is built with --with-pydebug. The debug allocator sticks two extra words at the beginning of each allocation, so the freelist pointers will overlap with those (this is also fine).

Here are two options:

  • Use relaxed stores in mimalloc, such as in mi_block_set_nextx. There's about six of these assignments -- not terrible to change -- but I don't love the idea of modifications to mimalloc that don't make sense to upstream, and these only make sense in the context of free threaded CPython.
  • Reorder PyObject in the free threading build so that ob_type is the first field. This avoids any overlap with ob_tid. It's annoying to break ABI or change the PyObject header though.

cc @mpage @Yhg1s

Linked PRs

  • gh-129748: Update mimalloc to use atomic store for mi_block_set_nextx #134238
  • [3.14] gh-129748: Update mimalloc to use atomic store for mi_block_set_nextx (GH-134238) #134352
  • [3.13] gh-129748: Update mimalloc to use atomic store for mi_block_set_nextx (GH-134238) #134353
  • gh-129748: Remove TSan suppression for mi_block_set_nextx #142887

Activity

Next
  1. colesbury
    added
    topic-free-threading
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Feb 6, 2025
  2. hawkinsp commented on Feb 6, 2025

    @hawkinsp
    hawkinsp
    on Feb 6, 2025
    Contributor
    More actions

    I think we saw this one too: https://gist.github.com/hawkinsp/948bc90fe8942f69db78924ffbb8a4eb but never figured out how to trigger it.

    Reacted by vfdev
  3. Yhg1s commented on Feb 6, 2025

    @Yhg1s
    Yhg1s
    on Feb 6, 2025
    Member
    More actions

    Couldn't we also just add an offsetting dummy item to the mi_block_t type, before the mi_encoded_t member? We have to avoid clashing with other offsets we read from the object struct in different build modes, but we have to be careful of that if we were to move the ob_tid field as well, and padding the mi_block_t struct means only one change to mimalloc. (That doesn't solve the other objection to modifying mimalloc this way, of course.)

  4. Yhg1s commented on Feb 7, 2025

    @Yhg1s
    Yhg1s
    on Feb 7, 2025
    Member
    More actions

    (Oh, unless blocks in mimalloc are allocation-sized, in which case that would break on pointer-sized allocations.)

  5. picnixz
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    on Feb 7, 2025
  6. Yhg1s commented on Feb 7, 2025

    @Yhg1s
    Yhg1s
    on Feb 7, 2025
    Member
    More actions

    Thinking about this a bit more: besides the (correct) TSan warning, realistically the problem here is that the read can lead to garbage being read as the ob_tid field. We're only comparing it against the current thread, so it feels very unlikely that the read will lead to a valid ob_tid, let alone the current thread's ob_tid. Moving around mimalloc's freelist pointer runs the same risk for other fields (e.g. we're loading ob_ref_local as part of the same lookup). Is it really worth changing the ABI for this?

  7. colesbury commented on Feb 7, 2025

    @colesbury
    colesbury
    on Feb 7, 2025
    ContributorAuthor
    More actions

    It's not garbage in the ob_tid field in the sense that it's not uninitialized memory or a completely arbitrary value. The freelist pointers are distinct from other pointers, including ob_tid. We rely on the same principle in the free threaded GC when we reuse ob_tid for worklists.

    ob_type is the only other field in PyObject that would be safe to overlap with the freelist pointer. That's because we don't read ob_type during _Py_TryIncrefCompare() and other similar functions.

    Moving around mimalloc's freelist pointer runs the same risk for other fields...

    Yeah, I don't think that moving the mimalloc freelist pointer will work well for multiple reasons. Some allocations may be pointer-sized, like you said. Pre-headers (managed dictionary/wekarefs) also make this more complicated.

    Reordering the fields in PyObject so that ob_type is first is much simpler.

    Is it really worth changing the ABI for this?

    I'm not sure. ABI breaking changes during alphas seems pretty normal. But using relaxed atomic writes in mimalloc isn't so bad either.

  8. Yhg1s commented on Feb 7, 2025

    @Yhg1s
    Yhg1s
    on Feb 7, 2025
    Member
    More actions

    It's not garbage in the ob_tid field in the sense that it's not uninitialized memory or a completely arbitrary value.

    I meant that in the current situation, with the non-atomic writes, the atomic reads can produce a value that's different from both the old value and the pointer written to it by mimalloc, isn't that true? (I forget if it's technically undefined or not.)

    Reacted by Sam Gross
  9. duaneg commented on Mar 3, 2025

    @duaneg
    duaneg
    on Mar 3, 2025
    Contributor
    More actions

    If it is useful, this reliably reproduces this on my machine: repro test case

  10. corona10 commented on Apr 27, 2025

    @corona10
    corona10
    on Apr 27, 2025
    Member
    More actions

    @colesbury @Yhg1s Do you have a decision for this issue? IMO, it should be decided before LS as much as possible. If it is delayed, I think that we should consider modifying the mimalloc implementation.

  11. corona10 commented on Apr 27, 2025

    @corona10
    corona10
    on Apr 27, 2025
    Member
    More actions

    cc @hugovk

  12. colesbury commented on Apr 28, 2025

    @colesbury
    colesbury
    on Apr 28, 2025
    ContributorAuthor
    More actions

    I'm still not sure what to do. Modifying the mimalloc implementation is probably the easiest.

    Reacted by Donghee Na
  13. corona10
    self-assigned this
    on May 2, 2025
  14. hawkinsp commented on May 13, 2025

    @hawkinsp
    hawkinsp
    on May 13, 2025
    Contributor
    More actions

    The LRU cache test added in #133787 seems to trigger this race with a high probability.

  15. 5 remaining items

  16. corona10
    added 2 commits that reference this issue on May 20, 2025

    [3.14] gh-129748: Update mimalloc to use atomic store for mi_block_se…

    dc5866a

    [3.13] gh-129748: Update mimalloc to use atomic store for mi_block_se…

    bad9f63
  17. corona10
    closed this as completedon May 20, 2025
  18. hawkinsp
    added a commit that references this issue on May 20, 2025

    Remove tsan suppression from python/cpython#129748

    0144ec1
  19. lkollar
    added a commit that references this issue on May 26, 2025

    pythongh-129748: Update mimalloc to use atomic store for mi_block_set…

    718505c
  20. charleshofer
    added a commit that references this issue on Jun 19, 2025

    Remove tsan suppression from python/cpython#129748

    3c092eb
  21. Pranjal095
    added a commit that references this issue on Jul 12, 2025

    pythongh-129748: Update mimalloc to use atomic store for mi_block_set…

    14fce87
  22. taegyunkim
    added a commit that references this issue on Aug 4, 2025

    pythongh-129748: Update mimalloc to use atomic store for mi_block_set…

    bf49758
  23. colesbury
    added 3 commits that reference this issue on Dec 17, 2025

    pythongh-129748: Remove suppression for mi_block_set_nextx

    d73d45b

    Merge branch 'main' into pythongh-129748-remove-suppression

    0e17b38

    gh-129748: Remove TSan suppression for mi_block_set_nextx (gh-142887)

    6e625f8
  24. corona10
    added 2 commits that reference this issue on Oct 10, 2026

    Re-apply CPython-specific changes to mimalloc v2.5.2

    fa3a143

    Re-apply CPython-specific changes to mimalloc v2.5.2

    836dced
  25. corona10
    added 2 commits that reference this issue on Oct 22, 2026

    gh-159111: Update vendored mimalloc to v2.5.2 (gh-159112)

    aff7a2e

    [3.15] pythongh-159111: Update vendored mimalloc to v2.5.2 (pythongh-…

    265f0fb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

  • @corona10
    corona10

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.