Skip to content

Commit 8bb2303

Browse files
authored
gh-99127: Allow some features of syslog to the main interpreter only (gh-99128)
1 parent ed39109 commit 8bb2303

File tree

5 files changed

+122
-2
lines changed

5 files changed

+122
-2
lines changed

Doc/library/syslog.rst

+21
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ The module defines the following functions:
4040
it wasn't called prior to the call to :func:`syslog`, deferring to the syslog
4141
implementation to call ``openlog()``.
4242

43+
.. versionchanged:: 3.12
44+
This function is restricted in subinterpreters.
45+
(Only code that runs in multiple interpreters is affected and
46+
the restriction is not relevant for most users.)
47+
:func:`openlog` must be called in the main interpreter before :func:`syslog` may be used
48+
in a subinterpreter. Otherwise it will raise :exc:`RuntimeError`.
49+
4350

4451
.. function:: openlog([ident[, logoption[, facility]]])
4552

@@ -60,6 +67,13 @@ The module defines the following functions:
6067
In previous versions, keyword arguments were not allowed, and *ident* was
6168
required.
6269

70+
.. versionchanged:: 3.12
71+
This function is restricted in subinterpreters.
72+
(Only code that runs in multiple interpreters is affected and
73+
the restriction is not relevant for most users.)
74+
This may only be called in the main interpreter.
75+
It will raise :exc:`RuntimeError` if called in a subinterpreter.
76+
6377

6478
.. function:: closelog()
6579

@@ -72,6 +86,13 @@ The module defines the following functions:
7286

7387
.. audit-event:: syslog.closelog "" syslog.closelog
7488

89+
.. versionchanged:: 3.12
90+
This function is restricted in subinterpreters.
91+
(Only code that runs in multiple interpreters is affected and
92+
the restriction is not relevant for most users.)
93+
This may only be called in the main interpreter.
94+
It will raise :exc:`RuntimeError` if called in a subinterpreter.
95+
7596

7697
.. function:: setlogmask(maskpri)
7798

Doc/whatsnew/3.12.rst

+9
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,15 @@ Changes in the Python API
673673
:class:`bytes` type is accepted for bytes strings.
674674
(Contributed by Victor Stinner in :gh:`98393`.)
675675

676+
* :func:`syslog.openlog` and :func:`syslog.closelog` now fail if used in subinterpreters.
677+
:func:`syslog.syslog` may still be used in subinterpreters,
678+
but now only if :func:`syslog.openlog` has already been called in the main interpreter.
679+
These new restrictions do not apply to the main interpreter,
680+
so only a very small set of users might be affected.
681+
This change helps with interpreter isolation. Furthermore, :mod:`syslog` is a wrapper
682+
around process-global resources, which are best managed from the main interpreter.
683+
(Contributed by Dong-hee Na in :gh:`99127`.)
684+
676685

677686
Build Changes
678687
=============

Lib/test/test_syslog.py

+64
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import threading
66
import time
77
import unittest
8+
from textwrap import dedent
89

910
# XXX(nnorwitz): This test sucks. I don't know of a platform independent way
1011
# to verify that the messages were really logged.
@@ -78,6 +79,69 @@ def logger():
7879
finally:
7980
sys.setswitchinterval(orig_si)
8081

82+
def test_subinterpreter_syslog(self):
83+
# syslog.syslog() is not allowed in subinterpreters, but only if
84+
# syslog.openlog() hasn't been called in the main interpreter yet.
85+
with self.subTest('before openlog()'):
86+
code = dedent('''
87+
import syslog
88+
caught_error = False
89+
try:
90+
syslog.syslog('foo')
91+
except RuntimeError:
92+
caught_error = True
93+
assert(caught_error)
94+
''')
95+
res = support.run_in_subinterp(code)
96+
self.assertEqual(res, 0)
97+
98+
syslog.openlog()
99+
try:
100+
with self.subTest('after openlog()'):
101+
code = dedent('''
102+
import syslog
103+
syslog.syslog('foo')
104+
''')
105+
res = support.run_in_subinterp(code)
106+
self.assertEqual(res, 0)
107+
finally:
108+
syslog.closelog()
109+
110+
def test_subinterpreter_openlog(self):
111+
try:
112+
code = dedent('''
113+
import syslog
114+
caught_error = False
115+
try:
116+
syslog.openlog()
117+
except RuntimeError:
118+
caught_error = True
119+
120+
assert(caught_error)
121+
''')
122+
res = support.run_in_subinterp(code)
123+
self.assertEqual(res, 0)
124+
finally:
125+
syslog.closelog()
126+
127+
def test_subinterpreter_closelog(self):
128+
syslog.openlog('python')
129+
try:
130+
code = dedent('''
131+
import syslog
132+
caught_error = False
133+
try:
134+
syslog.closelog()
135+
except RuntimeError:
136+
caught_error = True
137+
138+
assert(caught_error)
139+
''')
140+
res = support.run_in_subinterp(code)
141+
self.assertEqual(res, 0)
142+
finally:
143+
syslog.closelog()
144+
81145

82146
if __name__ == "__main__":
83147
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow some features of :mod:`syslog` to the main interpreter only. Patch by Dong-hee Na.

Modules/syslogmodule.c

+27-2
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,16 @@ module syslog
6161

6262
#include "clinic/syslogmodule.c.h"
6363

64-
/* only one instance, only one syslog, so globals should be ok */
65-
static PyObject *S_ident_o = NULL; /* identifier, held by openlog() */
64+
/* only one instance, only one syslog, so globals should be ok,
65+
* these fields are writable from the main interpreter only. */
66+
static PyObject *S_ident_o = NULL; // identifier, held by openlog()
6667
static char S_log_open = 0;
6768

69+
static inline int
70+
is_main_interpreter(void)
71+
{
72+
return (PyInterpreterState_Get() == PyInterpreterState_Main());
73+
}
6874

6975
static PyObject *
7076
syslog_get_argv(void)
@@ -135,6 +141,13 @@ syslog_openlog_impl(PyObject *module, PyObject *ident, long logopt,
135141
long facility)
136142
/*[clinic end generated code: output=5476c12829b6eb75 input=8a987a96a586eee7]*/
137143
{
144+
// Since the sys.openlog changes the process level state of syslog library,
145+
// this operation is only allowed for the main interpreter.
146+
if (!is_main_interpreter()) {
147+
PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.openlog()");
148+
return NULL;
149+
}
150+
138151
const char *ident_str = NULL;
139152

140153
if (ident) {
@@ -195,6 +208,11 @@ syslog_syslog_impl(PyObject *module, int group_left_1, int priority,
195208

196209
/* if log is not opened, open it now */
197210
if (!S_log_open) {
211+
if (!is_main_interpreter()) {
212+
PyErr_SetString(PyExc_RuntimeError, "subinterpreter can't use syslog.syslog() "
213+
"until the syslog is opened by the main interpreter");
214+
return NULL;
215+
}
198216
PyObject *openlog_ret = syslog_openlog_impl(module, NULL, 0, LOG_USER);
199217
if (openlog_ret == NULL) {
200218
return NULL;
@@ -229,6 +247,13 @@ static PyObject *
229247
syslog_closelog_impl(PyObject *module)
230248
/*[clinic end generated code: output=97890a80a24b1b84 input=fb77a54d447acf07]*/
231249
{
250+
// Since the sys.closelog changes the process level state of syslog library,
251+
// this operation is only allowed for the main interpreter.
252+
if (!is_main_interpreter()) {
253+
PyErr_SetString(PyExc_RuntimeError, "sunbinterpreter can't use syslog.closelog()");
254+
return NULL;
255+
}
256+
232257
if (PySys_Audit("syslog.closelog", NULL) < 0) {
233258
return NULL;
234259
}

0 commit comments

Comments
 (0)