mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
[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>
This commit is contained in:
parent
8fe0b1d8fa
commit
68f5fa6683
7 changed files with 87 additions and 13 deletions
|
@ -133,6 +133,21 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, PyObject **stack_pointer)
|
||||||
frame->stacktop = (int)(stack_pointer - frame->localsplus);
|
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
|
/* For use by _PyFrame_GetFrameObject
|
||||||
Do not call directly. */
|
Do not call directly. */
|
||||||
PyFrameObject *
|
PyFrameObject *
|
||||||
|
@ -144,6 +159,8 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame);
|
||||||
static inline PyFrameObject *
|
static inline PyFrameObject *
|
||||||
_PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
|
_PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
assert(!_PyFrame_IsIncomplete(frame));
|
||||||
PyFrameObject *res = frame->frame_obj;
|
PyFrameObject *res = frame->frame_obj;
|
||||||
if (res != NULL) {
|
if (res != NULL) {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -170,6 +170,43 @@ class GeneratorTest(unittest.TestCase):
|
||||||
g.send(0)
|
g.send(0)
|
||||||
self.assertEqual(next(g), 1)
|
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):
|
class ExceptionTest(unittest.TestCase):
|
||||||
# Tests for the issue #23353: check that the currently handled exception
|
# Tests for the issue #23353: check that the currently handled exception
|
||||||
|
|
|
@ -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.
|
|
@ -636,11 +636,10 @@ PyCode_New(int argcount, int kwonlyargcount,
|
||||||
exceptiontable);
|
exceptiontable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char assert0[4] = {
|
static const char assert0[6] = {
|
||||||
LOAD_ASSERTION_ERROR,
|
RESUME, 0,
|
||||||
0,
|
LOAD_ASSERTION_ERROR, 0,
|
||||||
RAISE_VARARGS,
|
RAISE_VARARGS, 1
|
||||||
1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PyCodeObject *
|
PyCodeObject *
|
||||||
|
@ -664,7 +663,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
|
||||||
if (filename_ob == NULL) {
|
if (filename_ob == NULL) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
code_ob = PyBytes_FromStringAndSize(assert0, 4);
|
code_ob = PyBytes_FromStringAndSize(assert0, 6);
|
||||||
if (code_ob == NULL) {
|
if (code_ob == NULL) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1155,8 +1155,14 @@ PyFrame_GetBack(PyFrameObject *frame)
|
||||||
{
|
{
|
||||||
assert(frame != NULL);
|
assert(frame != NULL);
|
||||||
PyFrameObject *back = frame->f_back;
|
PyFrameObject *back = frame->f_back;
|
||||||
if (back == NULL && frame->f_frame->previous != NULL) {
|
if (back == NULL) {
|
||||||
back = _PyFrame_GetFrameObject(frame->f_frame->previous);
|
_PyInterpreterFrame *prev = frame->f_frame->previous;
|
||||||
|
while (prev && _PyFrame_IsIncomplete(prev)) {
|
||||||
|
prev = prev->previous;
|
||||||
|
}
|
||||||
|
if (prev) {
|
||||||
|
back = _PyFrame_GetFrameObject(prev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Py_XINCREF(back);
|
Py_XINCREF(back);
|
||||||
return back;
|
return back;
|
||||||
|
|
|
@ -66,9 +66,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
|
||||||
f->f_frame = frame;
|
f->f_frame = frame;
|
||||||
frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
|
frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
|
||||||
assert(f->f_back == NULL);
|
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 */
|
/* Link PyFrameObjects.f_back and remove link through _PyInterpreterFrame.previous */
|
||||||
PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous);
|
PyFrameObject *back = _PyFrame_GetFrameObject(prev);
|
||||||
if (back == NULL) {
|
if (back == NULL) {
|
||||||
/* Memory error here. */
|
/* Memory error here. */
|
||||||
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
|
assert(PyErr_ExceptionMatches(PyExc_MemoryError));
|
||||||
|
|
|
@ -1784,9 +1784,17 @@ sys__getframe_impl(PyObject *module, int depth)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (depth > 0 && frame != NULL) {
|
if (frame != NULL) {
|
||||||
frame = frame->previous;
|
while (depth > 0) {
|
||||||
--depth;
|
frame = frame->previous;
|
||||||
|
if (frame == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (_PyFrame_IsIncomplete(frame)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
--depth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (frame == NULL) {
|
if (frame == NULL) {
|
||||||
_PyErr_SetString(tstate, PyExc_ValueError,
|
_PyErr_SetString(tstate, PyExc_ValueError,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue