From 5891758bec7ce8ac3ff64a2eb002f7db9b603712 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 21 Nov 2017 10:33:16 -0700 Subject: [PATCH 1/3] Add a pre-init test using Py_DecodeLocale(). --- Lib/test/test_capi.py | 10 ++++++++++ Programs/_testembed.c | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index bb5b2a3b9f0d73..2fe0feca5a3ca4 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -593,6 +593,16 @@ def test_forced_io_encoding(self): self.maxDiff = None self.assertEqual(out.strip(), expected_output) + def test_pre_initialization_api(self): + """ + Checks the few parts of the C-API that work before the runtine + is initialized (via Py_Initialize()). + """ + env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path)) + out, err = self.run_embedded_interpreter("pre_initialization_api", env=env) + self.assertEqual(out, '') + self.assertEqual(err, '') + class SkipitemTest(unittest.TestCase): diff --git a/Programs/_testembed.c b/Programs/_testembed.c index e68e68de327b12..dc27a40b6e4330 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -72,6 +72,7 @@ static int test_repeated_init_and_subinterpreters(void) return 0; } + /***************************************************** * Test forcing a particular IO encoding *****************************************************/ @@ -125,6 +126,27 @@ static int test_forced_io_encoding(void) return 0; } +/********************************************************* + * Test parts of the C-API that work before initialization + *********************************************************/ + +static int test_pre_initialization_api(void) +{ + wchar_t *program = Py_DecodeLocale("spam", NULL); + if (program == NULL) { + fprintf(stderr, "Fatal error: cannot decode program name\n"); + return 1; + } + Py_SetProgramName(program); + + Py_Initialize(); + Py_Finalize(); + + PyMem_RawFree(program); + return 0; +} + + /* ********************************************************* * List of test cases and the function that implements it. * @@ -146,6 +168,7 @@ struct TestCase static struct TestCase TestCases[] = { { "forced_io_encoding", test_forced_io_encoding }, { "repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters }, + { "pre_initialization_api", test_pre_initialization_api }, { NULL, NULL } }; From 7f6420411a0374b97e9d86e67273b1e23d8880be Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 21 Nov 2017 10:46:52 -0700 Subject: [PATCH 2/3] Add pre-init defaults for PyMem_RawMalloc() and PyMem_RawFree(). --- Objects/obmalloc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 7c6973ec035f10..ea601643fc92ef 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -421,6 +421,11 @@ PyMem_RawMalloc(size_t size) */ if (size > (size_t)PY_SSIZE_T_MAX) return NULL; + // If this is used before the Python runtime is initialized, fall + // back to the default behavior. + if (_PyMem_Raw.malloc == NULL) { + return _PyMem_RawMalloc(NULL, size); + } return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); } @@ -445,6 +450,12 @@ PyMem_RawRealloc(void *ptr, size_t new_size) void PyMem_RawFree(void *ptr) { + // If this is used before the Python runtime is initialized, fall + // back to the default behavior. + if (_PyMem_Raw.free == NULL) { + _PyMem_RawFree(NULL, ptr); + return; + } _PyMem_Raw.free(_PyMem_Raw.ctx, ptr); } From 175bea2a0dce9a428ebe3877a64997a83c40cc82 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 21 Nov 2017 12:45:19 -0700 Subject: [PATCH 3/3] Add a Misc/NEWS entry. --- .../2017-11-21-12-44-51.bpo-32096.tXX68e.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-11-21-12-44-51.bpo-32096.tXX68e.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-11-21-12-44-51.bpo-32096.tXX68e.rst b/Misc/NEWS.d/next/Core and Builtins/2017-11-21-12-44-51.bpo-32096.tXX68e.rst new file mode 100644 index 00000000000000..67687d3d659386 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2017-11-21-12-44-51.bpo-32096.tXX68e.rst @@ -0,0 +1,6 @@ +Since the recent global runtime state consolidation, calls to +PyMem_RawMalloc() and PyMem_RawFree() made before runtime initialization +have been crashing. This is because they rely on the raw memory allocator +having been initialized already. Before the runtime state change the +default raw allocator was initialized statically. This has now been fixed +by falling back to the defaults in PyMem_RawMalloc() and PyMem_RawFree().