Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbaca85f

Browse files
xdegayeserhiy-storchaka
authored andcommitted
[2.7]bpo-17288: Prevent jumps from 'return' and 'exception' trace events. (GH-6111)
(cherry picked from commite32bbaf)
1 parent3854f58 commitbaca85f

File tree

3 files changed

+93
-15
lines changed

3 files changed

+93
-15
lines changed

‎Lib/test/test_sys_settrace.py‎

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -482,20 +482,35 @@ def g(frame, why, extra):
482482
classJumpTracer:
483483
"""Defines a trace function that jumps from one place to another."""
484484

485-
def__init__(self,function,jumpFrom,jumpTo):
486-
self.function=function
485+
def__init__(self,function,jumpFrom,jumpTo,event='line',
486+
decorated=False):
487+
self.code=function.func_code
487488
self.jumpFrom=jumpFrom
488489
self.jumpTo=jumpTo
490+
self.event=event
491+
self.firstLine=Noneifdecoratedelseself.code.co_firstlineno
489492
self.done=False
490493

491494
deftrace(self,frame,event,arg):
492-
ifnotself.doneandframe.f_code==self.function.func_code:
493-
firstLine=frame.f_code.co_firstlineno
494-
ifevent=='line'andframe.f_lineno==firstLine+self.jumpFrom:
495+
ifself.done:
496+
return
497+
# frame.f_code.co_firstlineno is the first line of the decorator when
498+
# 'function' is decorated and the decorator may be written using
499+
# multiple physical lines when it is too long. Use the first line
500+
# trace event in 'function' to find the first line of 'function'.
501+
if (self.firstLineisNoneandframe.f_code==self.codeand
502+
event=='line'):
503+
self.firstLine=frame.f_lineno-1
504+
if (event==self.eventandself.firstLineand
505+
frame.f_lineno==self.firstLine+self.jumpFrom):
506+
f=frame
507+
whilefisnotNoneandf.f_code!=self.code:
508+
f=f.f_back
509+
iffisnotNone:
495510
# Cope with non-integer self.jumpTo (because of
496511
# no_jump_to_non_integers below).
497512
try:
498-
frame.f_lineno=firstLine+self.jumpTo
513+
frame.f_lineno=self.firstLine+self.jumpTo
499514
exceptTypeError:
500515
frame.f_lineno=self.jumpTo
501516
self.done=True
@@ -535,8 +550,9 @@ def compare_jump_output(self, expected, received):
535550
"Expected: "+repr(expected)+"\n"+
536551
"Received: "+repr(received))
537552

538-
defrun_test(self,func,jumpFrom,jumpTo,expected,error=None):
539-
tracer=JumpTracer(func,jumpFrom,jumpTo)
553+
defrun_test(self,func,jumpFrom,jumpTo,expected,error=None,
554+
event='line',decorated=False):
555+
tracer=JumpTracer(func,jumpFrom,jumpTo,event,decorated)
540556
sys.settrace(tracer.trace)
541557
output= []
542558
iferrorisNone:
@@ -547,15 +563,15 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None):
547563
sys.settrace(None)
548564
self.compare_jump_output(expected,output)
549565

550-
defjump_test(jumpFrom,jumpTo,expected,error=None):
566+
defjump_test(jumpFrom,jumpTo,expected,error=None,event='line'):
551567
"""Decorator that creates a test that makes a jump
552568
from one place to another in the following code.
553569
"""
554570
defdecorator(func):
555571
@wraps(func)
556572
deftest(self):
557-
# +1 to compensate a decorator line
558-
self.run_test(func,jumpFrom+1,jumpTo+1,expected,error)
573+
self.run_test(func,jumpFrom,jumpTo,expected,
574+
error=error,event=event,decorated=True)
559575
returntest
560576
returndecorator
561577

@@ -1018,6 +1034,36 @@ class fake_function:
10181034
sys.settrace(None)
10191035
self.compare_jump_output([2,3,2,3,4],namespace["output"])
10201036

1037+
@jump_test(2,3, [1],event='call',error=(ValueError,"can't jump from"
1038+
" the 'call' trace event of a new frame"))
1039+
deftest_no_jump_from_call(output):
1040+
output.append(1)
1041+
defnested():
1042+
output.append(3)
1043+
nested()
1044+
output.append(5)
1045+
1046+
@jump_test(2,1, [1],event='return',error=(ValueError,
1047+
"can only jump from a 'line' trace event"))
1048+
deftest_no_jump_from_return_event(output):
1049+
output.append(1)
1050+
return
1051+
1052+
@jump_test(2,1, [1],event='exception',error=(ValueError,
1053+
"can only jump from a 'line' trace event"))
1054+
deftest_no_jump_from_exception_event(output):
1055+
output.append(1)
1056+
1/0
1057+
1058+
@jump_test(3,2, [2],event='return',error=(ValueError,
1059+
"can't jump from a yield statement"))
1060+
deftest_no_jump_from_yield(output):
1061+
defgen():
1062+
output.append(2)
1063+
yield3
1064+
next(gen())
1065+
output.append(5)
1066+
10211067

10221068
deftest_main():
10231069
test_support.run_unittest(
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Prevent jumps from 'return' and 'exception' trace events.

‎Objects/frameobject.c‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ frame_getlineno(PyFrameObject *f, void *closure)
8989
* o 'try'/'for'/'while' blocks can't be jumped into because the blockstack
9090
* needs to be set up before their code runs, and for 'for' loops the
9191
* iterator needs to be on the stack.
92+
* o Jumps cannot be made from within a trace function invoked with a
93+
* 'return' or 'exception' event since the eval loop has been exited at
94+
* that time.
9295
*/
9396
staticint
9497
frame_setlineno(PyFrameObject*f,PyObject*p_new_lineno)
@@ -122,13 +125,32 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
122125
return-1;
123126
}
124127

128+
/* Upon the 'call' trace event of a new frame, f->f_lasti is -1 and
129+
* f->f_trace is NULL, check first on the first condition.
130+
* Forbidding jumps from the 'call' event of a new frame is a side effect
131+
* of allowing to set f_lineno only from trace functions. */
132+
if (f->f_lasti==-1) {
133+
PyErr_Format(PyExc_ValueError,
134+
"can't jump from the 'call' trace event of a new frame");
135+
return-1;
136+
}
137+
125138
/* You can only do this from within a trace function, not via
126139
* _getframe or similar hackery. */
127-
if (!f->f_trace)
128-
{
140+
if (!f->f_trace) {
129141
PyErr_Format(PyExc_ValueError,
130-
"f_lineno can only be set by a"
131-
" line trace function");
142+
"f_lineno can only be set by a trace function");
143+
return-1;
144+
}
145+
146+
/* Forbid jumps upon a 'return' trace event (except after executing a
147+
* YIELD_VALUE opcode, f_stacktop is not NULL in that case) and upon an
148+
* 'exception' trace event.
149+
* Jumps from 'call' trace events have already been forbidden above for new
150+
* frames, so this check does not change anything for 'call' events. */
151+
if (f->f_stacktop==NULL) {
152+
PyErr_SetString(PyExc_ValueError,
153+
"can only jump from a 'line' trace event");
132154
return-1;
133155
}
134156

@@ -178,6 +200,15 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno)
178200
min_addr=MIN(new_lasti,f->f_lasti);
179201
max_addr=MAX(new_lasti,f->f_lasti);
180202

203+
/* The trace function is called with a 'return' trace event after the
204+
* execution of a yield statement. */
205+
assert(f->f_lasti!=-1);
206+
if (code[f->f_lasti]==YIELD_VALUE) {
207+
PyErr_SetString(PyExc_ValueError,
208+
"can't jump from a yield statement");
209+
return-1;
210+
}
211+
181212
/* You can't jump onto a line with an 'except' statement on it -
182213
* they expect to have an exception on the top of the stack, which
183214
* won't be true if you jump to them. They always start with code

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp