diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2022-09-22 17:17:53 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-22 17:17:53 (GMT) |
commit | 6a646dd1ffa9ccc9130e09fdd2f5b75652c4f121 (patch) | |
tree | ceadad1e80c6efe52ee40af59d622198b9d6a12d | |
parent | 773dbb9e3a7dc5d4a8560bc3ffb28c16758f159f (diff) | |
download | cpython-6a646dd1ffa9ccc9130e09fdd2f5b75652c4f121.zip cpython-6a646dd1ffa9ccc9130e09fdd2f5b75652c4f121.tar.gz cpython-6a646dd1ffa9ccc9130e09fdd2f5b75652c4f121.tar.bz2 |
GH-96975: Skip incomplete frames in PyEval_GetFrame (GH-97018)
(cherry picked from commit 8fd2c3b75b90c4ee391894aa5094615bbdb6242f)
Co-authored-by: Brandt Bucher <brandtbucher@microsoft.com>
-rw-r--r-- | Lib/test/test_embed.py | 3 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-09-21-16-06-37.gh-issue-96975.BmE0XY.rst | 2 | ||||
-rw-r--r-- | Programs/_testembed.c | 68 | ||||
-rw-r--r-- | Python/ceval.c | 9 |
4 files changed, 79 insertions, 3 deletions
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 90022db..b8e3c37 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1709,6 +1709,9 @@ class AuditingTests(EmbeddingTestsMixin, unittest.TestCase): timeout=support.SHORT_TIMEOUT, returncode=1) + def test_get_incomplete_frame(self): + self.run_embedded_interpreter("test_get_incomplete_frame") + class MiscTests(EmbeddingTestsMixin, unittest.TestCase): def test_unicode_id_init(self): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-21-16-06-37.gh-issue-96975.BmE0XY.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-21-16-06-37.gh-issue-96975.BmE0XY.rst new file mode 100644 index 0000000..e6fcb84 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-21-16-06-37.gh-issue-96975.BmE0XY.rst @@ -0,0 +1,2 @@ +Fix a crash occurring when :c:func:`PyEval_GetFrame` is called while the +topmost Python frame is in a partially-initialized state. diff --git a/Programs/_testembed.c b/Programs/_testembed.c index a2a2ff4..e5b138c 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -1950,6 +1950,73 @@ static int test_repeated_init_and_inittab(void) return 0; } +static void wrap_allocator(PyMemAllocatorEx *allocator); +static void unwrap_allocator(PyMemAllocatorEx *allocator); + +static void * +malloc_wrapper(void *ctx, size_t size) +{ + PyMemAllocatorEx *allocator = (PyMemAllocatorEx *)ctx; + unwrap_allocator(allocator); + PyEval_GetFrame(); // BOOM! + wrap_allocator(allocator); + return allocator->malloc(allocator->ctx, size); +} + +static void * +calloc_wrapper(void *ctx, size_t nelem, size_t elsize) +{ + PyMemAllocatorEx *allocator = (PyMemAllocatorEx *)ctx; + return allocator->calloc(allocator->ctx, nelem, elsize); +} + +static void * +realloc_wrapper(void *ctx, void *ptr, size_t new_size) +{ + PyMemAllocatorEx *allocator = (PyMemAllocatorEx *)ctx; + return allocator->realloc(allocator->ctx, ptr, new_size); +} + +static void +free_wrapper(void *ctx, void *ptr) +{ + PyMemAllocatorEx *allocator = (PyMemAllocatorEx *)ctx; + allocator->free(allocator->ctx, ptr); +} + +static void +wrap_allocator(PyMemAllocatorEx *allocator) +{ + PyMem_GetAllocator(PYMEM_DOMAIN_OBJ, allocator); + PyMemAllocatorEx wrapper = { + .malloc = &malloc_wrapper, + .calloc = &calloc_wrapper, + .realloc = &realloc_wrapper, + .free = &free_wrapper, + .ctx = allocator, + }; + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &wrapper); +} + +static void +unwrap_allocator(PyMemAllocatorEx *allocator) +{ + PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, allocator); +} + +static int +test_get_incomplete_frame(void) +{ + _testembed_Py_Initialize(); + PyMemAllocatorEx allocator; + wrap_allocator(&allocator); + // Force an allocation with an incomplete (generator) frame: + int result = PyRun_SimpleString("(_ for _ in ())"); + unwrap_allocator(&allocator); + Py_Finalize(); + return result; +} + /* ********************************************************* * List of test cases and the function that implements it. @@ -2032,6 +2099,7 @@ static struct TestCase TestCases[] = { #ifndef MS_WINDOWS {"test_frozenmain", test_frozenmain}, #endif + {"test_get_incomplete_frame", test_get_incomplete_frame}, {NULL, NULL} }; diff --git a/Python/ceval.c b/Python/ceval.c index 07a5461..c5283ac 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -7116,11 +7116,14 @@ _PyEval_GetFrame(void) PyFrameObject * PyEval_GetFrame(void) { - PyThreadState *tstate = _PyThreadState_GET(); - if (tstate->cframe->current_frame == NULL) { + _PyInterpreterFrame *frame = _PyEval_GetFrame(); + while (frame && _PyFrame_IsIncomplete(frame)) { + frame = frame->previous; + } + if (frame == NULL) { return NULL; } - PyFrameObject *f = _PyFrame_GetFrameObject(tstate->cframe->current_frame); + PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f == NULL) { PyErr_Clear(); } |