summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2022-07-01 10:08:20 (GMT)
committerGitHub <noreply@github.com>2022-07-01 10:08:20 (GMT)
commit544531de23d69f5b23883794fc7bb23a958a0fcb (patch)
treead88c0a6543127cd89027843ab441aa29dbdbc75
parent1df9449db24f16c9c96bdd7dc283a5062bca68e6 (diff)
downloadcpython-544531de23d69f5b23883794fc7bb23a958a0fcb.zip
cpython-544531de23d69f5b23883794fc7bb23a958a0fcb.tar.gz
cpython-544531de23d69f5b23883794fc7bb23a958a0fcb.tar.bz2
GH-94262: Don't create frame objects for frames that aren't yet complete. (GH-94371)
-rw-r--r--Include/internal/pycore_frame.h17
-rw-r--r--Lib/test/test_generators.py37
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-06-28-10-08-06.gh-issue-94262.m-HWUZ.rst3
-rw-r--r--Objects/codeobject.c11
-rw-r--r--Objects/frameobject.c10
-rw-r--r--Python/frame.c8
-rw-r--r--Python/sysmodule.c14
7 files changed, 87 insertions, 13 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index eed26fb..994c205 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -134,6 +134,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 *
@@ -145,6 +160,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 6f2a837..d4fa0e3 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -638,11 +638,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 *
@@ -666,7 +665,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 9ff0443..44cc062 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -1169,8 +1169,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 206ecab..7c6705e 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -68,9 +68,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 444042f..9312007 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -1776,9 +1776,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,