Skip to content

Commit 425b022

Browse files
committed
gh-151218: Replace sys.flags in PyConfig_Set()
PyConfig_Set() and sys.set_int_max_str_digits() now replace sys.flags, 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.
1 parent 6b217ea commit 425b022

3 files changed

Lines changed: 68 additions & 17 deletions

File tree

Lib/test/test_capi/test_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,11 @@ def expect_bool_not(value):
356356
for value in new_values:
357357
expected, expect_flag = expect_func(value)
358358

359+
old_flags = sys.flags
359360
config_set(name, value)
360361
self.assertEqual(config_get(name), expected)
361362
self.assertEqual(getattr(sys.flags, sys_flag), expect_flag)
363+
self.assertIsNot(sys.flags, old_flags)
362364
if name == "write_bytecode":
363365
self.assertEqual(getattr(sys, "dont_write_bytecode"),
364366
expect_flag)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:c:func:`PyConfig_Set()` and :func:`sys.set_int_max_str_digits` now replace
2+
:data:`sys.flags`, instead of modifying :data:`sys.flags` in-place. Patch by
3+
Victor Stinner.

Python/sysmodule.c

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,7 +1874,8 @@ sys_get_int_max_str_digits_impl(PyObject *module)
18741874
/*[clinic end generated code: output=0042f5e8ae0e8631 input=77fb74e987ba7ecb]*/
18751875
{
18761876
PyInterpreterState *interp = _PyInterpreterState_GET();
1877-
return PyLong_FromLong(interp->long_state.max_str_digits);
1877+
int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits);
1878+
return PyLong_FromLong(maxdigits);
18781879
}
18791880

18801881

@@ -3490,14 +3491,50 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value)
34903491
int
34913492
_PySys_SetFlagObj(Py_ssize_t pos, PyObject *value)
34923493
{
3493-
PyObject *flags = PySys_GetAttrString("flags");
3494-
if (flags == NULL) {
3495-
return -1;
3494+
PyObject *old_flags = NULL;
3495+
PyObject *new_flags = NULL;
3496+
PyObject *flags_str = NULL;
3497+
3498+
flags_str = PyUnicode_FromString("flags");
3499+
if (flags_str == NULL) {
3500+
goto error;
34963501
}
34973502

3498-
sys_set_flag(flags, pos, value);
3499-
Py_DECREF(flags);
3500-
return 0;
3503+
old_flags = PySys_GetAttr(flags_str);
3504+
if (old_flags == NULL) {
3505+
goto error;
3506+
}
3507+
3508+
new_flags = PyStructSequence_New(&FlagsType);
3509+
if (new_flags == NULL) {
3510+
goto error;
3511+
}
3512+
3513+
for (Py_ssize_t i=0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) {
3514+
if (i != pos) {
3515+
PyObject *old_value;
3516+
old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref
3517+
if (old_value == NULL) {
3518+
goto error;
3519+
}
3520+
sys_set_flag(new_flags, i, old_value);
3521+
}
3522+
else {
3523+
sys_set_flag(new_flags, pos, value);
3524+
}
3525+
}
3526+
3527+
int res = _PySys_SetAttr(flags_str, new_flags);
3528+
Py_DECREF(flags_str);
3529+
Py_DECREF(old_flags);
3530+
Py_DECREF(new_flags);
3531+
return res;
3532+
3533+
error:
3534+
Py_DECREF(flags_str);
3535+
Py_DECREF(old_flags);
3536+
Py_DECREF(new_flags);
3537+
return -1;
35013538
}
35023539

35033540

@@ -3521,8 +3558,6 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
35213558
const PyPreConfig *preconfig = &interp->runtime->preconfig;
35223559
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
35233560

3524-
// _PySys_UpdateConfig() modifies sys.flags in-place:
3525-
// Py_XDECREF() is needed in this case.
35263561
Py_ssize_t pos = 0;
35273562
#define SetFlagObj(expr) \
35283563
do { \
@@ -4153,16 +4188,27 @@ _PySys_UpdateConfig(PyThreadState *tstate)
41534188
#undef COPY_LIST
41544189
#undef COPY_WSTR
41554190

4156-
// sys.flags
4157-
PyObject *flags = PySys_GetAttrString("flags");
4158-
if (flags == NULL) {
4191+
// replace sys.flags
4192+
PyObject *new_flags = PyStructSequence_New(&FlagsType);
4193+
if (new_flags == NULL) {
41594194
return -1;
41604195
}
4161-
if (set_flags_from_config(interp, flags) < 0) {
4162-
Py_DECREF(flags);
4196+
if (set_flags_from_config(interp, new_flags) < 0) {
4197+
Py_DECREF(new_flags);
4198+
return -1;
4199+
}
4200+
4201+
PyObject *flags_str = PyUnicode_FromString("flags");
4202+
if (flags_str == NULL) {
4203+
Py_DECREF(new_flags);
4204+
return -1;
4205+
}
4206+
res = _PySys_SetAttr(flags_str, new_flags);
4207+
Py_DECREF(new_flags);
4208+
Py_DECREF(flags_str);
4209+
if (res < 0) {
41634210
return -1;
41644211
}
4165-
Py_DECREF(flags);
41664212

41674213
SET_SYS("dont_write_bytecode", PyBool_FromLong(!config->write_bytecode));
41684214

@@ -4675,7 +4721,7 @@ _PySys_SetIntMaxStrDigits(int maxdigits)
46754721
// Set PyInterpreterState.long_state.max_str_digits
46764722
// and PyInterpreterState.config.int_max_str_digits.
46774723
PyInterpreterState *interp = _PyInterpreterState_GET();
4678-
interp->long_state.max_str_digits = maxdigits;
4679-
interp->config.int_max_str_digits = maxdigits;
4724+
_Py_atomic_store_int(&interp->long_state.max_str_digits, maxdigits);
4725+
_Py_atomic_store_int(&interp->config.int_max_str_digits, maxdigits);
46804726
return 0;
46814727
}

0 commit comments

Comments
 (0)