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 in `_PyImport_SetDLOpenFlags` when `sys.setdlopenflags()` is called concurrently · Issue #151644 · python/cpython

Data race in _PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently #151644

New issue
Copy link
New issue
Copy link
Closed
Closed
Data race in _PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently#151644
Copy link
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

@Naserume
Naserume
opened on Jun 18, 2026
Issue body actions

Bug report

Bug description:

In the free-threaded build, calling sys.setdlopenflags() concurrently from multiple threads (or concurrently with sys.getdlopenflags()) data-races on the per-interpreter dlopenflags field: _PyImport_GetDLOpenFlags / _PyImport_SetDLOpenFlags read and write (interp)->imports.dlopenflags with no atomics or lock.

cpython/Python/import.c

Lines 908 to 918 in 9e863fa

int
_PyImport_GetDLOpenFlags(PyInterpreterState *interp)
{
return DLOPENFLAGS(interp);
}
void
_PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val)
{
DLOPENFLAGS(interp) = new_val;
}

sys.setdlopenflags() / sys.getdlopenflags() expose these directly, so concurrent calls produce a non-atomic write/write (and read/write) race on the field.

Reproducer:

import sys
from threading import Thread, Barrier

N = 12
barrier = Barrier(N)

def worker():
    barrier.wait()
    for _ in range(20000):
        d = sys.getdlopenflags()
        sys.setdlopenflags(d)

if __name__ == "__main__":
    threads = [Thread(target=worker) for _ in range(N)]
    for t in threads: t.start()
    for t in threads: t.join()

TSAN Report:

==================
WARNING: ThreadSanitizer: data race (pid=2296999)
  Read of size 4 at 0x555555e3f648 by thread T1:
    #0 _PyImport_GetDLOpenFlags /cpython/Python/import.c:911:12 
    #1 sys_getdlopenflags_impl /cpython/./Python/sysmodule.c:1838:13 
    #2 sys_getdlopenflags /cpython/./Python/clinic/sysmodule.c.h:823:12 
    #3 cfunction_vectorcall_NOARGS /cpython/Objects/methodobject.c:508:24 
    #4 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #5 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #6 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #7 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1846:35 
    ...

  Previous write of size 4 at 0x555555e3f648 by thread T12:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Location is global '_PyRuntime' of size 405824 at 0x555555e16c80 

SUMMARY: ThreadSanitizer: data race /cpython/Python/import.c:911:12 in _PyImport_GetDLOpenFlags
==================
==================
WARNING: ThreadSanitizer: data race (pid=2296999)
  Write of size 4 at 0x555555e3f648 by thread T12:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Previous write of size 4 at 0x555555e3f648 by thread T9:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Location is global '_PyRuntime' of size 405824 at 0x555555e16c80 

SUMMARY: ThreadSanitizer: data race /cpython/Python/import.c:917:25 in _PyImport_SetDLOpenFlags
==================

CPython versions tested on:

3.16

Operating systems tested on:

Linux

Linked PRs

  • gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading #151768
  • [3.14] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading (gh-151768) #153789
  • [3.15] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading (gh-151768) #153790

Activity

  1. Naserume
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Jun 18, 2026
  2. ZeroIntensity
    added
    interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
    topic-free-threading
    on Jun 18, 2026
  3. zangjiucheng
    added a commit that references this issue on Jun 20, 2026

    pythongh-151644: Fix data race in sys.setdlopenflags/getdlopenflags u…

    99b6011
  4. zangjiucheng commented on Jun 20, 2026

    @zangjiucheng
    zangjiucheng
    on Jun 20, 2026
    Contributor
    More actions

    Hey Everyone,

    Similar to gh-151646, this is a data race in free-threading builds between
    concurrent sys.getdlopenflags() (reader) and sys.setdlopenflags() (writer)
    on interp->imports.dlopenflags.

    Fix: use FT_ATOMIC_LOAD_INT_RELAXED / FT_ATOMIC_STORE_INT_RELAXED in
    _PyImport_GetDLOpenFlags / _PyImport_SetDLOpenFlags.

    We chose atomics over PyMutex (used in gh-151646) because dlopenflags is a
    single int field — each access is exactly one load or one store. A mutex would
    be overkill. Relaxed ordering is sufficient: this is a standalone config integer
    with no ordering relationship to other memory, consistent with how
    lazy_imports_mode and pystats_enabled are protected in the same codebase.

    Verified on Linux x86-64 free-threading build: 12 threads × 20,000 concurrent
    get/set iterations exit cleanly.

    Let me know if some suggestion :)

  5. zangjiucheng
    added a commit that references this issue on Jun 20, 2026

    Misc/NEWS.d: fix news entry filename format for pythongh-151644

    f75b2c7
  6. nascheme
    added a commit that references this issue on Jul 3, 2026

    gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under f…

    cde31ec
  7. nascheme commented on Jul 3, 2026

    @nascheme
    nascheme
    on Jul 3, 2026
    Member
    More actions

    Thank you for the PR.

  8. nascheme
    closed this as completedon Jul 3, 2026
  9. kumaraditya303
    added a commit that references this issue on Jul 16, 2026

    [3.15] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags …

    9fcebd3
  10. nascheme
    added a commit that references this issue on Jul 17, 2026

    [3.14] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags …

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

Metadata

Metadata

Assignees

No one assigned

    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.