summaryrefslogtreecommitdiff
path: root/Objects/frameobject.c
diff options
context:
space:
mode:
authorxdegaye <xdegaye@gmail.com>2018-03-13 22:06:14 +0100
committerSerhiy Storchaka <storchaka@gmail.com>2018-03-13 23:06:14 +0200
commitbaca85fcc7cdf70af4a76ea0966d4842c173de1a (patch)
treedd43035a6abae0cb2b350c56a180348e43b86a6e /Objects/frameobject.c
parent3854f5885edc8dc67b1aba82fbb525604fbc625b (diff)
downloadcpython-git-baca85fcc7cdf70af4a76ea0966d4842c173de1a.tar.gz
[2.7] bpo-17288: Prevent jumps from 'return' and 'exception' trace events. (GH-6111)
(cherry picked from commit e32bbaf376a09c149fa7c7f2919d7c9ce4e2a055)
Diffstat (limited to 'Objects/frameobject.c')
-rw-r--r--Objects/frameobject.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index bf1c7c5273..175874503b 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -89,6 +89,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)
@@ -122,13 +125,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 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;
}
@@ -178,6 +200,15 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
min_addr = MIN(new_lasti, f->f_lasti);
max_addr = MAX(new_lasti, f->f_lasti);
+ /* 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) {
+ 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