Skip to content

Commit 89a79fc

Browse files
[3.12] pythongh-130163: Fix crashes related to PySys_GetObject() (pythonGH-130503) (pythonGH-130556) (pythonGH-130576)
The use of PySys_GetObject() and _PySys_GetAttr(), which return a borrowed reference, has been replaced by using one of the following functions, which return a strong reference and distinguish a missing attribute from an error: _PySys_GetOptionalAttr(), _PySys_GetOptionalAttrString(), _PySys_GetRequiredAttr(), and _PySys_GetRequiredAttrString(). (cherry picked from commit 0ef4ffe) (cherry picked from commit 7c1b76f) (cherry picked from commit 2ab7e11)
1 parent 6a268a0 commit 89a79fc

21 files changed

+504
-160
lines changed

Include/internal/pycore_sysmodule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11+
PyAPI_FUNC(int) _PySys_GetOptionalAttr(PyObject *, PyObject **);
12+
PyAPI_FUNC(int) _PySys_GetOptionalAttrString(const char *, PyObject **);
13+
PyAPI_FUNC(PyObject *) _PySys_GetRequiredAttr(PyObject *);
14+
PyAPI_FUNC(PyObject *) _PySys_GetRequiredAttrString(const char *);
15+
1116
PyAPI_FUNC(int) _PySys_Audit(
1217
PyThreadState *tstate,
1318
const char *event,

Lib/test/test_builtin.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,29 @@ def test_input(self):
15261526
sys.stdout = savestdout
15271527
fp.close()
15281528

1529+
def test_input_gh130163(self):
1530+
class X(io.StringIO):
1531+
def __getattribute__(self, name):
1532+
nonlocal patch
1533+
if patch:
1534+
patch = False
1535+
sys.stdout = X()
1536+
sys.stderr = X()
1537+
sys.stdin = X('input\n')
1538+
support.gc_collect()
1539+
return io.StringIO.__getattribute__(self, name)
1540+
1541+
with (support.swap_attr(sys, 'stdout', None),
1542+
support.swap_attr(sys, 'stderr', None),
1543+
support.swap_attr(sys, 'stdin', None)):
1544+
patch = False
1545+
# the only references:
1546+
sys.stdout = X()
1547+
sys.stderr = X()
1548+
sys.stdin = X('input\n')
1549+
patch = True
1550+
input() # should not crash
1551+
15291552
# test_int(): see test_int.py for tests of built-in function int().
15301553

15311554
def test_repr(self):

Lib/test/test_print.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ def flush(self):
129129
raise RuntimeError
130130
self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True)
131131

132+
def test_gh130163(self):
133+
class X:
134+
def __str__(self):
135+
sys.stdout = StringIO()
136+
support.gc_collect()
137+
return 'foo'
138+
139+
with support.swap_attr(sys, 'stdout', None):
140+
sys.stdout = StringIO() # the only reference
141+
print(X()) # should not crash
142+
132143

133144
class TestPy2MigrationHint(unittest.TestCase):
134145
"""Test that correct hint is produced analogous to Python3 syntax,

Lib/test/test_sys.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import builtins
22
import codecs
33
import gc
4+
import io
45
import locale
56
import operator
67
import os
@@ -79,6 +80,18 @@ def baddisplayhook(obj):
7980
code = compile("42", "<string>", "single")
8081
self.assertRaises(ValueError, eval, code)
8182

83+
def test_gh130163(self):
84+
class X:
85+
def __repr__(self):
86+
sys.stdout = io.StringIO()
87+
support.gc_collect()
88+
return 'foo'
89+
90+
with support.swap_attr(sys, 'stdout', None):
91+
sys.stdout = io.StringIO() # the only reference
92+
sys.displayhook(X()) # should not crash
93+
94+
8295
class ActiveExceptionTests(unittest.TestCase):
8396
def test_exc_info_no_exception(self):
8497
self.assertEqual(sys.exc_info(), (None, None, None))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix possible crashes related to concurrent
2+
change and use of the :mod:`sys` module attributes.

Modules/_cursesmodule.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ static const char PyCursesVersion[] = "2.2";
109109
#include "Python.h"
110110
#include "pycore_long.h" // _PyLong_GetZero()
111111
#include "pycore_structseq.h" // _PyStructSequence_NewType()
112+
#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString()
112113

113114
#ifdef __hpux
114115
#define STRICT_SYSV_CURSES
@@ -3375,17 +3376,20 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd)
33753376
if (fd == -1) {
33763377
PyObject* sys_stdout;
33773378

3378-
sys_stdout = PySys_GetObject("stdout");
3379+
if (_PySys_GetOptionalAttrString("stdout", &sys_stdout) < 0) {
3380+
return NULL;
3381+
}
33793382

33803383
if (sys_stdout == NULL || sys_stdout == Py_None) {
33813384
PyErr_SetString(
33823385
PyCursesError,
33833386
"lost sys.stdout");
3387+
Py_XDECREF(sys_stdout);
33843388
return NULL;
33853389
}
33863390

33873391
fd = PyObject_AsFileDescriptor(sys_stdout);
3388-
3392+
Py_DECREF(sys_stdout);
33893393
if (fd == -1) {
33903394
return NULL;
33913395
}

Modules/_pickle.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "pycore_moduleobject.h" // _PyModule_GetState()
1414
#include "pycore_runtime.h" // _Py_ID()
1515
#include "pycore_pystate.h" // _PyThreadState_GET()
16+
#include "pycore_sysmodule.h" // _PySys_GetRequiredAttr()
1617
#include "structmember.h" // PyMemberDef
1718

1819
#include <stdlib.h> // strtol()
@@ -1984,49 +1985,54 @@ whichmodule(PyObject *global, PyObject *dotted_path)
19841985
assert(module_name == NULL);
19851986

19861987
/* Fallback on walking sys.modules */
1987-
PyThreadState *tstate = _PyThreadState_GET();
1988-
modules = _PySys_GetAttr(tstate, &_Py_ID(modules));
1988+
modules = _PySys_GetRequiredAttr(&_Py_ID(modules));
19891989
if (modules == NULL) {
1990-
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
19911990
return NULL;
19921991
}
19931992
if (PyDict_CheckExact(modules)) {
19941993
i = 0;
19951994
while (PyDict_Next(modules, &i, &module_name, &module)) {
19961995
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
1996+
Py_DECREF(modules);
19971997
return Py_NewRef(module_name);
19981998
}
19991999
if (PyErr_Occurred()) {
2000+
Py_DECREF(modules);
20002001
return NULL;
20012002
}
20022003
}
20032004
}
20042005
else {
20052006
PyObject *iterator = PyObject_GetIter(modules);
20062007
if (iterator == NULL) {
2008+
Py_DECREF(modules);
20072009
return NULL;
20082010
}
20092011
while ((module_name = PyIter_Next(iterator))) {
20102012
module = PyObject_GetItem(modules, module_name);
20112013
if (module == NULL) {
20122014
Py_DECREF(module_name);
20132015
Py_DECREF(iterator);
2016+
Py_DECREF(modules);
20142017
return NULL;
20152018
}
20162019
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
20172020
Py_DECREF(module);
20182021
Py_DECREF(iterator);
2022+
Py_DECREF(modules);
20192023
return module_name;
20202024
}
20212025
Py_DECREF(module);
20222026
Py_DECREF(module_name);
20232027
if (PyErr_Occurred()) {
20242028
Py_DECREF(iterator);
2029+
Py_DECREF(modules);
20252030
return NULL;
20262031
}
20272032
}
20282033
Py_DECREF(iterator);
20292034
}
2035+
Py_DECREF(modules);
20302036

20312037
/* If no module is found, use __main__. */
20322038
module_name = &_Py_ID(__main__);

Modules/_threadmodule.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "pycore_moduleobject.h" // _PyModule_GetState()
88
#include "pycore_pylifecycle.h"
99
#include "pycore_pystate.h" // _PyThreadState_SetCurrent()
10+
#include "pycore_sysmodule.h" // _PySys_GetOptionalAttr()
11+
1012
#include <stddef.h> // offsetof()
1113
#include "structmember.h" // PyMemberDef
1214

@@ -1567,9 +1569,12 @@ thread_excepthook(PyObject *module, PyObject *args)
15671569
PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2);
15681570
PyObject *thread = PyStructSequence_GET_ITEM(args, 3);
15691571

1570-
PyThreadState *tstate = _PyThreadState_GET();
1571-
PyObject *file = _PySys_GetAttr(tstate, &_Py_ID(stderr));
1572+
PyObject *file;
1573+
if (_PySys_GetOptionalAttr( &_Py_ID(stderr), &file) < 0) {
1574+
return NULL;
1575+
}
15721576
if (file == NULL || file == Py_None) {
1577+
Py_XDECREF(file);
15731578
if (thread == Py_None) {
15741579
/* do nothing if sys.stderr is None and thread is None */
15751580
Py_RETURN_NONE;
@@ -1586,9 +1591,6 @@ thread_excepthook(PyObject *module, PyObject *args)
15861591
Py_RETURN_NONE;
15871592
}
15881593
}
1589-
else {
1590-
Py_INCREF(file);
1591-
}
15921594

15931595
int res = thread_excepthook_file(file, exc_type, exc_value, exc_tb,
15941596
thread);

Modules/_tkinter.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Copyright (C) 1994 Steen Lumholt.
3333
#endif
3434

3535
#include "pycore_long.h"
36+
#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString()
3637

3738
#ifdef MS_WINDOWS
3839
#include <windows.h>
@@ -154,9 +155,11 @@ _get_tcl_lib_path(void)
154155
/* Check expected location for an installed Python first */
155156
tcl_library_path = PyUnicode_FromString("\\tcl\\tcl" TCL_VERSION);
156157
if (tcl_library_path == NULL) {
158+
Py_DECREF(prefix);
157159
return NULL;
158160
}
159161
tcl_library_path = PyUnicode_Concat(prefix, tcl_library_path);
162+
Py_DECREF(prefix);
160163
if (tcl_library_path == NULL) {
161164
return NULL;
162165
}
@@ -3509,6 +3512,7 @@ PyInit__tkinter(void)
35093512
uexe = PyUnicode_FromWideChar(Py_GetProgramName(), -1);
35103513
if (uexe) {
35113514
cexe = PyUnicode_EncodeFSDefault(uexe);
3515+
Py_DECREF(uexe);
35123516
if (cexe) {
35133517
#ifdef MS_WINDOWS
35143518
int set_var = 0;
@@ -3521,12 +3525,14 @@ PyInit__tkinter(void)
35213525
if (!ret && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
35223526
str_path = _get_tcl_lib_path();
35233527
if (str_path == NULL && PyErr_Occurred()) {
3528+
Py_DECREF(cexe);
35243529
Py_DECREF(m);
35253530
return NULL;
35263531
}
35273532
if (str_path != NULL) {
35283533
wcs_path = PyUnicode_AsWideCharString(str_path, NULL);
35293534
if (wcs_path == NULL) {
3535+
Py_DECREF(cexe);
35303536
Py_DECREF(m);
35313537
return NULL;
35323538
}
@@ -3546,7 +3552,6 @@ PyInit__tkinter(void)
35463552
#endif /* MS_WINDOWS */
35473553
}
35483554
Py_XDECREF(cexe);
3549-
Py_DECREF(uexe);
35503555
}
35513556

35523557
if (PyErr_Occurred()) {

0 commit comments

Comments
 (0)