Skip to content

Commit 224b4b0

Browse files
gh-158929: Fix subinterpreter thread safety issues in syslog (GH-159000)
1 parent 565f76f commit 224b4b0

3 files changed

Lines changed: 31 additions & 10 deletions

File tree

‎Lib/test/test_syslog.py‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,29 @@ def test_subinterpreter_closelog(self):
142142
finally:
143143
syslog.closelog()
144144

145+
@threading_helper.requires_working_threading()
146+
def test_subinterpreter_concurrent_syslog(self):
147+
# gh-158929: This used to crash.
148+
from concurrent import interpreters
149+
150+
interp = interpreters.create()
151+
try:
152+
def in_interpreter():
153+
import syslog
154+
155+
for _ in range(40000):
156+
try:
157+
syslog.syslog('my hovercraft is full of eels')
158+
except RuntimeError:
159+
pass
160+
thread = interp.call_in_thread(in_interpreter)
161+
for i in range(40000):
162+
syslog.openlog(f"python{i}")
163+
syslog.closelog()
164+
thread.join()
165+
finally:
166+
interp.close()
167+
145168

146169
if __name__ == "__main__":
147170
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when calling :func:`syslog.syslog` while a subinterpreter in
2+
another thread calls :func:`syslog.openlog` or :func:`syslog.closelog`.

‎Modules/syslogmodule.c‎

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ module syslog
6969
/* only one instance, only one syslog, so globals should be ok,
7070
* these fields are writable from the main interpreter only. */
7171
static PyObject *S_ident_o = NULL; // identifier, held by openlog()
72-
static char S_log_open = 0;
72+
static int S_log_open = 0;
7373

7474
static inline int
7575
is_main_interpreter(void)
@@ -189,7 +189,7 @@ syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt,
189189
}
190190

191191
openlog(ident_str, logopt, facility);
192-
S_log_open = 1;
192+
_Py_atomic_store_int(&S_log_open, 1);
193193
Py_XSETREF(S_ident_o, ident);
194194

195195
Py_RETURN_NONE;
@@ -222,7 +222,7 @@ syslog_syslog_impl(PyObject *module, int group_left_1, int priority,
222222
}
223223

224224
/* if log is not opened, open it now */
225-
if (!S_log_open) {
225+
if (!_Py_atomic_load_int(&S_log_open)) {
226226
if (!is_main_interpreter()) {
227227
PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.syslog() "
228228
"until the syslog is opened by the main interpreter");
@@ -235,10 +235,6 @@ syslog_syslog_impl(PyObject *module, int group_left_1, int priority,
235235
Py_DECREF(openlog_ret);
236236
}
237237

238-
/* Incref ident, because it can be decrefed if syslog.openlog() is
239-
* called when the GIL is released.
240-
*/
241-
PyObject *ident = Py_XNewRef(S_ident_o);
242238
#ifdef __APPLE__
243239
// gh-98178: On macOS, libc syslog() is not thread-safe
244240
syslog(priority, "%s", message);
@@ -247,7 +243,6 @@ syslog_syslog_impl(PyObject *module, int group_left_1, int priority,
247243
syslog(priority, "%s", message);
248244
Py_END_ALLOW_THREADS;
249245
#endif
250-
Py_XDECREF(ident);
251246
Py_RETURN_NONE;
252247
}
253248

@@ -273,10 +268,11 @@ syslog_closelog_impl(PyObject *module)
273268
if (PySys_Audit("syslog.closelog", NULL) < 0) {
274269
return NULL;
275270
}
276-
if (S_log_open) {
271+
272+
int was_open = _Py_atomic_exchange_int(&S_log_open, 0);
273+
if (was_open) {
277274
closelog();
278275
Py_CLEAR(S_ident_o);
279-
S_log_open = 0;
280276
}
281277
Py_RETURN_NONE;
282278
}

0 commit comments

Comments
 (0)