Skip to content

gh-151218: Fix data race in sys_set_flag for free-threading#151220

Open
bhuvi27 wants to merge 1 commit into
python:mainfrom
bhuvi27:gh-151218-fix-sys-set-flag-race
Open

gh-151218: Fix data race in sys_set_flag for free-threading#151220
bhuvi27 wants to merge 1 commit into
python:mainfrom
bhuvi27:gh-151218-fix-sys-set-flag-race

Conversation

@bhuvi27

@bhuvi27 bhuvi27 commented Jun 10, 2026

Copy link
Copy Markdown

Fixes #151218

Concurrent calls to sys.set_int_max_str_digits() in free-threaded builds
could double-free the same sys.flags tuple item because sys_set_flag()
updated the slot without synchronization.

Protect sys.flags updates with a mutex in free-threaded builds, and hold
the same lock across flag and interpreter int_max_str_digits state updates
so sys.get_int_max_str_digits() stays consistent with sys.flags. Add a
concurrent stress regression test in test_sys.py.

Concurrent calls to sys.set_int_max_str_digits() in free-threaded builds
could double-free the same sys.flags tuple item because sys_set_flag()
updated the slot without synchronization.

Protect sys.flags updates with a mutex in free-threaded builds and hold
the same lock across the flag and interpreter int_max_str_digits state
updates so sys.get_int_max_str_digits() stays consistent with sys.flags.
@bhuvi27 bhuvi27 force-pushed the gh-151218-fix-sys-set-flag-race branch from 099518a to 09f17c8 Compare June 10, 2026 06:58
@picnixz

picnixz commented Jun 10, 2026

Copy link
Copy Markdown
Member

Please do not force push. To contribute cleanly, please read https://devguide.python.org/getting-started/pull-request-lifecycle/#pullrequest.

@da-woods

da-woods commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This doesn't guard the read though. And the read through sys.flags is probably difficult to guard because it's an "immutable" object and so shouldn't be changing once it's visible

@bhuvi27

bhuvi27 commented Jun 10, 2026

Copy link
Copy Markdown
Author

This doesn't guard the read though. And the read through sys.flags is probably difficult to guard because it's an "immutable" object and so shouldn't be changing once it's visible

Thanks for the pointer — I force-pushed while fixing CI and cleaning up commits. I’ll use additive commits from here on and have read the PR lifecycle guide.

@bhuvi27

bhuvi27 commented Jun 10, 2026

Copy link
Copy Markdown
Author

This doesn't guard the read though. And the read through sys.flags is probably difficult to guard because it's an "immutable" object and so shouldn't be changing once it's visible

Good point — you're right that the read path through sys.flags isn't
guarded by the new mutex, so a reader can still observe the old slot
while a writer replaces and decref's it (use-after-free).

A few options I see:

  1. Stop updating sys.flags.int_max_str_digits at runtime entirely —
    treat sys.flags as startup-only (its documented immutability),
    and let sys.get_int_max_str_digits() be the single source of truth
    for the current value. This removes the racy shared object.

  2. Atomic store/load of the slot pointer plus deferred reclamation
    (QSBR) for the old value — keeps the current API but is more
    machinery.

  3. Replace sys.flags with a fresh struct sequence on each update.

Option 1 looks cleanest and matches the "immutable" intent, but it's
a behaviour change. Which direction would you prefer? Happy to redo
the PR around option 1 if that's the right call.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Data race in sys_set_flag when sys.set_int_max_str_digits() is called concurrently with free-threaded build

3 participants