diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2022-07-04 18:43:12 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-04 18:43:12 (GMT) |
commit | 68f5fa668343341b79ce1e23f1d9e773b98fd312 (patch) | |
tree | bc9b4ab897d1addc94ebf52c643e720fb75944d7 | |
parent | 8fe0b1d8fa3451e923d7632263be6145a0734468 (diff) | |
download | cpython-68f5fa668343341b79ce1e23f1d9e773b98fd312.zip cpython-68f5fa668343341b79ce1e23f1d9e773b98fd312.tar.gz cpython-68f5fa668343341b79ce1e23f1d9e773b98fd312.tar.bz2 |
[3.11] GH-94262: Don't create frame objects for frames that aren't yet complete. (GH-94371) (#94482)
Co-authored-by: Mark Shannon <mark@hotpy.org>
-rw-r--r-- | Include/internal/pycore_frame.h | 17 | ||||
-rw-r--r-- | Lib/test/test_generators.py | 37 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst | 3 | ||||
-rw-r--r-- | Objects/codeobject.c | 11 | ||||
-rw-r--r-- | Objects/frameobject.c | 10 | ||||
-rw-r--r-- | Python/frame.c | 8 | ||||
-rw-r--r-- | Python/sysmodule.c | 14 |
7 files changed, 87 insertions, 13 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 405afd6..efdcdbc 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -133,6 +133,21 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer) frame->stacktop = (int)(stack_pointer - frame->localsplus); } +/* Determine whether a frame is incomplete. + * A frame is incomplete if it is part way through + * creating cell objects or a generator or coroutine. + * + * Frames on the frame stack are incomplete until the + * first RESUME instruction. + * Frames owned by a generator are always complete. + */ +static inline bool +_PyFrame_IsIncomplete(_PyInterpreterFrame *frame) +{ + return frame->owner != FRAME_OWNED_BY_GENERATOR && + frame->prev_instr < _PyCode_CODE(frame->f_code) + frame->f_code->_co_firsttraceable; +} + /* For use by _PyFrame_GetFrameObject Do not call directly. */ PyFrameObject * @@ -144,6 +159,8 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame); static inline PyFrameObject * _PyFrame_GetFrameObject(_PyInterpreterFrame *frame) { + + assert(!_PyFrame_IsIncomplete(frame)); PyFrameObject *res = frame->frame_obj; if (res != NULL) { return res; diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 87a7dd6..e5aa7da 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -170,6 +170,43 @@ class GeneratorTest(unittest.TestCase): g.send(0) self.assertEqual(next(g), 1) + def test_handle_frame_object_in_creation(self): + + #Attempt to expose partially constructed frames + #See https://github.com/python/cpython/issues/94262 + + def cb(*args): + inspect.stack() + + def gen(): + yield 1 + + thresholds = gc.get_threshold() + + gc.callbacks.append(cb) + gc.set_threshold(1, 0, 0) + try: + gen() + finally: + gc.set_threshold(*thresholds) + gc.callbacks.pop() + + class Sneaky: + def __del__(self): + inspect.stack() + + sneaky = Sneaky() + sneaky._s = Sneaky() + sneaky._s._s = sneaky + + gc.set_threshold(1, 0, 0) + try: + del sneaky + gen() + finally: + gc.set_threshold(*thresholds) + + class ExceptionTest(unittest.TestCase): # Tests for the issue #23353: check that the currently handled exception diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst new file mode 100644 index 0000000..7ba39bb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst @@ -0,0 +1,3 @@ +Don't create frame objects for incomplete frames. Prevents the creation of +generators and closures from being observable to Python and C extensions, +restoring the behavior of 3.10 and earlier. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 2471006..4859b1e 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -636,11 +636,10 @@ PyCode_New(int argcount, int kwonlyargcount, exceptiontable); } -static const char assert0[4] = { - LOAD_ASSERTION_ERROR, - 0, - RAISE_VARARGS, - 1 +static const char assert0[6] = { + RESUME, 0, + LOAD_ASSERTION_ERROR, 0, + RAISE_VARARGS, 1 }; PyCodeObject * @@ -664,7 +663,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) if (filename_ob == NULL) { goto failed; } - code_ob = PyBytes_FromStringAndSize(assert0, 4); + code_ob = PyBytes_FromStringAndSize(assert0, 6); if (code_ob == NULL) { goto failed; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 85cc4a2..cb822a1 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1155,8 +1155,14 @@ PyFrame_GetBack(PyFrameObject *frame) { assert(frame != NULL); PyFrameObject *back = frame->f_back; - if (back == NULL && frame->f_frame->previous != NULL) { - back = _PyFrame_GetFrameObject(frame->f_frame->previous); + if (back == NULL) { + _PyInterpreterFrame *prev = frame->f_frame->previous; + while (prev && _PyFrame_IsIncomplete(prev)) { + prev = prev->previous; + } + if (prev) { + back = _PyFrame_GetFrameObject(prev); + } } Py_XINCREF(back); return back; diff --git a/Python/frame.c b/Python/frame.c index b6674ed..c4e9349 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -66,9 +66,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) f->f_frame = frame; frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; assert(f->f_back == NULL); - if (frame->previous != NULL) { + _PyInterpreterFrame *prev = frame->previous; + while (prev && _PyFrame_IsIncomplete(prev)) { + prev = prev->previous; + } + if (prev) { /* Link PyFrameObjects.f_back and remove link through _PyInterpreterFrame.previous */ - PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous); + PyFrameObject *back = _PyFrame_GetFrameObject(prev); if (back == NULL) { /* Memory error here. */ assert(PyErr_ExceptionMatches(PyExc_MemoryError)); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 7698641..261ebb9 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1784,9 +1784,17 @@ sys__getframe_impl(PyObject *module, int depth) return NULL; } - while (depth > 0 && frame != NULL) { - frame = frame->previous; - --depth; + if (frame != NULL) { + while (depth > 0) { + frame = frame->previous; + if (frame == NULL) { + break; + } + if (_PyFrame_IsIncomplete(frame)) { + continue; + } + --depth; + } } if (frame == NULL) { _PyErr_SetString(tstate, PyExc_ValueError, |