Skip to content

Commit e01d44b

Browse files
committed
gh-151673: Fix crash in warnings.warn() under memory pressure
1 parent 2e5843e commit e01d44b

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

Lib/test/test_warnings/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import warnings as original_warnings
2525
from warnings import deprecated
2626

27-
2827
py_warnings = import_helper.import_fresh_module('_py_warnings')
2928
py_warnings._set_module(py_warnings)
3029

@@ -1237,6 +1236,25 @@ def test_issue31566(self):
12371236
support.swap_item(globals(), '__file__', None):
12381237
self.assertRaises(UserWarning, self.module.warn, 'bar')
12391238

1239+
@support.cpython_only
1240+
# Python built with Py_TRACE_REFS fail with a fatal error in
1241+
# _PyRefchain_Trace() on memory allocation error.
1242+
@unittest.skipIf(support.Py_TRACE_REFS, 'cannot test Py_TRACE_REFS build')
1243+
def test_issue151673(self):
1244+
# Skip this test if the _testcapi module isn't available.
1245+
_testcapi = import_helper.import_module('_testcapi')
1246+
# warn() shouldn't crash when the "<sys>" fallback filename
1247+
# can't be allocated under memory pressure.
1248+
code = """if 1:
1249+
import _testcapi, warnings
1250+
warnings.simplefilter("always")
1251+
_testcapi.set_nomemory(0)
1252+
warnings.warn("boom")
1253+
"""
1254+
rc, out, err = assert_python_failure("-c", code)
1255+
self.assertIn(rc, (1, 120))
1256+
self.assertIn(b'MemoryError', err)
1257+
12401258

12411259
class WarningsDisplayTests(BaseTest):
12421260

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash in :func:`warnings.warn` when the ``"<sys>"`` fallback filename
2+
could not be allocated under memory pressure: :c:func:`!setup_context` left
3+
the filename ``NULL`` and it was later passed to :c:func:`Py_DECREF`.

Python/_warnings.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,9 +1031,18 @@ setup_context(Py_ssize_t stack_level,
10311031
}
10321032
}
10331033

1034+
/* Initialize the output references so handle_error can Py_XDECREF them
1035+
even when we bail out before they are assigned. */
1036+
*filename = NULL;
1037+
*registry = NULL;
1038+
*module = NULL;
1039+
10341040
if (f == NULL) {
10351041
globals = interp->sysdict;
10361042
*filename = PyUnicode_FromString("<sys>");
1043+
if (*filename == NULL) {
1044+
goto handle_error;
1045+
}
10371046
*lineno = 0;
10381047
}
10391048
else {
@@ -1043,8 +1052,6 @@ setup_context(Py_ssize_t stack_level,
10431052
Py_DECREF(f);
10441053
}
10451054

1046-
*module = NULL;
1047-
10481055
/* Setup registry. */
10491056
assert(globals != NULL);
10501057
assert(PyAnyDict_Check(globals));
@@ -1084,7 +1091,7 @@ setup_context(Py_ssize_t stack_level,
10841091
handle_error:
10851092
Py_XDECREF(*registry);
10861093
Py_XDECREF(*module);
1087-
Py_DECREF(*filename);
1094+
Py_XDECREF(*filename);
10881095
return 0;
10891096
}
10901097

0 commit comments

Comments
 (0)