diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2018-03-13 10:12:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-13 10:12:16 (GMT) |
commit | cf61a81f1d600064be6dd43896afcf5f976de9b0 (patch) | |
tree | d414fc7e4dddc5bd5df017f1b6ce300899016fcc /Objects/frameobject.c | |
parent | 6a526f673878677032c02f7800ee13d4769f391a (diff) | |
download | cpython-cf61a81f1d600064be6dd43896afcf5f976de9b0.zip cpython-cf61a81f1d600064be6dd43896afcf5f976de9b0.tar.gz cpython-cf61a81f1d600064be6dd43896afcf5f976de9b0.tar.bz2 |
[3.7] bpo-17288: Prevent jumps from 'return' and 'exception' trace events. (GH-5928)
(cherry picked from commit e32bbaf376a09c149fa7c7f2919d7c9ce4e2a055)
Co-authored-by: xdegaye <xdegaye@gmail.com>
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r-- | Objects/frameobject.c | 40 |
1 files changed, 36 insertions, 4 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 5c73e85..b7a16ad 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -56,6 +56,9 @@ frame_getlineno(PyFrameObject *f, void *closure) * o 'try'/'for'/'while' blocks can't be jumped into because the blockstack * needs to be set up before their code runs, and for 'for' loops 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) @@ -91,13 +94,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; } @@ -156,6 +178,16 @@ 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; + } + min_addr = Py_MIN(new_lasti, f->f_lasti); max_addr = Py_MAX(new_lasti, f->f_lasti); |