Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-96975: Skip incomplete frames in PyEval_GetFrame #97003

Merged
merged 4 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1713,6 +1713,9 @@ def test_audit_run_stdin(self):
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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
67 changes: 67 additions & 0 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -1950,6 +1950,72 @@ 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;
return allocator->free(allocator->ctx, ptr);
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
}

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()
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
{
_testembed_Py_Initialize();
PyMemAllocatorEx allocator;
wrap_allocator(&allocator);
// Force an allocation with an incomplete (generator) frame:
int result = PyRun_SimpleString("(_ for _ in ())");
Py_Finalize();
brandtbucher marked this conversation as resolved.
Show resolved Hide resolved
return result;
}


/* *********************************************************
* List of test cases and the function that implements it.
Expand Down Expand Up @@ -2032,6 +2098,7 @@ static struct TestCase TestCases[] = {
#ifndef MS_WINDOWS
{"test_frozenmain", test_frozenmain},
#endif
{"test_get_incomplete_frame", test_get_incomplete_frame},

{NULL, NULL}
};
Expand Down
9 changes: 6 additions & 3 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -6520,11 +6520,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();
}
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
Expand Down