mirror of
https://github.com/python/cpython.git
synced 2025-11-25 04:34:37 +00:00
bpo-17288: Prevent jumps from 'return' and 'exception' trace events. (GH-6107)
(cherry picked from commit e32bbaf376)
This commit is contained in:
parent
5affd5c29e
commit
b8e9d6c5cd
3 changed files with 93 additions and 15 deletions
|
|
@ -81,6 +81,9 @@ get_arg(const _Py_CODEUNIT *codestr, Py_ssize_t i)
|
|||
* the blockstack needs to be set up before their code runs.
|
||||
* o 'for' and 'async for' loops can't be jumped into because the
|
||||
* iterator needs to be on the stack.
|
||||
* o Jumps cannot be made from within a trace function invoked with a
|
||||
* 'return' or 'exception' event since the eval loop has been exited at
|
||||
* that time.
|
||||
*/
|
||||
static int
|
||||
frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
|
||||
|
|
@ -109,13 +112,32 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Upon the 'call' trace event of a new frame, f->f_lasti is -1 and
|
||||
* f->f_trace is NULL, check first on the first condition.
|
||||
* Forbidding jumps from the 'call' event of a new frame is a side effect
|
||||
* of allowing to set f_lineno only from trace functions. */
|
||||
if (f->f_lasti == -1) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"can't jump from the 'call' trace event of a new frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* You can only do this from within a trace function, not via
|
||||
* _getframe or similar hackery. */
|
||||
if (!f->f_trace)
|
||||
{
|
||||
if (!f->f_trace) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"f_lineno can only be set by a"
|
||||
" line trace function");
|
||||
"f_lineno can only be set by a trace function");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Forbid jumps upon a 'return' trace event (except after executing a
|
||||
* YIELD_VALUE or YIELD_FROM opcode, f_stacktop is not NULL in that case)
|
||||
* and upon an 'exception' trace event.
|
||||
* Jumps from 'call' trace events have already been forbidden above for new
|
||||
* frames, so this check does not change anything for 'call' events. */
|
||||
if (f->f_stacktop == NULL) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can only jump from a 'line' trace event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -175,6 +197,15 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
|
|||
/* We're now ready to look at the bytecode. */
|
||||
PyBytes_AsStringAndSize(f->f_code->co_code, (char **)&code, &code_len);
|
||||
|
||||
/* The trace function is called with a 'return' trace event after the
|
||||
* execution of a yield statement. */
|
||||
assert(f->f_lasti != -1);
|
||||
if (code[f->f_lasti] == YIELD_VALUE || code[f->f_lasti] == YIELD_FROM) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"can't jump from a yield statement");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* You can't jump onto a line with an 'except' statement on it -
|
||||
* they expect to have an exception on the top of the stack, which
|
||||
* won't be true if you jump to them. They always start with code
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue