gh-151218: Replace sys.flags in PyConfig_Set() (#151402)

PyConfig_Set() and sys.set_int_max_str_digits() now replace
sys.flags (create a new object), instead of modifying sys.flags in-place.

Modifying sys.flags in-place can lead to data races when multiple
threads are reading or writing sys.flags in parallel.

Use _Py_atomic functions to get and set max_str_digits members.

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
Victor Stinner
2026-06-16 18:17:33 +02:00
committed by GitHub
parent 2ce260033b
commit b16d23fc9f
9 changed files with 111 additions and 23 deletions
+47 -18
View File
@@ -1874,7 +1874,8 @@ sys_get_int_max_str_digits_impl(PyObject *module)
/*[clinic end generated code: output=0042f5e8ae0e8631 input=77fb74e987ba7ecb]*/
{
PyInterpreterState *interp = _PyInterpreterState_GET();
return PyLong_FromLong(interp->long_state.max_str_digits);
int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits);
return PyLong_FromLong(maxdigits);
}
@@ -3490,14 +3491,39 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value)
int
_PySys_SetFlagObj(Py_ssize_t pos, PyObject *value)
{
PyObject *flags = PySys_GetAttrString("flags");
if (flags == NULL) {
return -1;
PyObject *new_flags = NULL;
PyObject *flags_str = &_Py_ID(flags); // immortal ref
PyObject *old_flags = PySys_GetAttr(flags_str);
if (old_flags == NULL) {
goto error;
}
sys_set_flag(flags, pos, value);
Py_DECREF(flags);
return 0;
new_flags = PyStructSequence_New(&FlagsType);
if (new_flags == NULL) {
goto error;
}
for (Py_ssize_t i = 0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) {
if (i != pos) {
PyObject *old_value;
old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref
sys_set_flag(new_flags, i, old_value);
}
else {
sys_set_flag(new_flags, pos, value);
}
}
int res = _PySys_SetAttr(flags_str, new_flags);
Py_DECREF(old_flags);
Py_DECREF(new_flags);
return res;
error:
Py_XDECREF(old_flags);
Py_XDECREF(new_flags);
return -1;
}
@@ -3521,8 +3547,6 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
const PyPreConfig *preconfig = &interp->runtime->preconfig;
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
// _PySys_UpdateConfig() modifies sys.flags in-place:
// Py_XDECREF() is needed in this case.
Py_ssize_t pos = 0;
#define SetFlagObj(expr) \
do { \
@@ -4033,7 +4057,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict)
/* implementation */
SET_SYS("implementation", make_impl_info(version_info));
// sys.flags: updated in-place later by _PySys_UpdateConfig()
// sys.flags: updated later by _PySys_UpdateConfig()
ENSURE_INFO_TYPE(FlagsType, flags_desc);
SET_SYS("flags", make_flags(tstate->interp));
@@ -4153,16 +4177,21 @@ _PySys_UpdateConfig(PyThreadState *tstate)
#undef COPY_LIST
#undef COPY_WSTR
// sys.flags
PyObject *flags = PySys_GetAttrString("flags");
if (flags == NULL) {
// replace sys.flags
PyObject *new_flags = PyStructSequence_New(&FlagsType);
if (new_flags == NULL) {
return -1;
}
if (set_flags_from_config(interp, flags) < 0) {
Py_DECREF(flags);
if (set_flags_from_config(interp, new_flags) < 0) {
Py_DECREF(new_flags);
return -1;
}
res = _PySys_SetAttr(&_Py_ID(flags), new_flags);
Py_DECREF(new_flags);
if (res < 0) {
return -1;
}
Py_DECREF(flags);
SET_SYS("dont_write_bytecode", PyBool_FromLong(!config->write_bytecode));
@@ -4675,7 +4704,7 @@ _PySys_SetIntMaxStrDigits(int maxdigits)
// Set PyInterpreterState.long_state.max_str_digits
// and PyInterpreterState.config.int_max_str_digits.
PyInterpreterState *interp = _PyInterpreterState_GET();
interp->long_state.max_str_digits = maxdigits;
interp->config.int_max_str_digits = maxdigits;
_Py_atomic_store_int(&interp->long_state.max_str_digits, maxdigits);
_Py_atomic_store_int(&interp->config.int_max_str_digits, maxdigits);
return 0;
}