Skip to content

Commit a909007

Browse files
committed
py/objgenerator: Check stack before resuming a generator
This turns a hard crash in a recursive generator into a 'maximum recursion depth exceeded' exception.
1 parent 3215b85 commit a909007

3 files changed

Lines changed: 10 additions & 1 deletion

File tree

py/objgenerator.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "py/bc.h"
3333
#include "py/objgenerator.h"
3434
#include "py/objfun.h"
35+
#include "py/stackctrl.h"
3536

3637
/******************************************************************************/
3738
/* generator wrapper */
@@ -92,6 +93,7 @@ STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_pri
9293
}
9394

9495
mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) {
96+
MP_STACK_CHECK();
9597
mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_gen_instance));
9698
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
9799
if (self->code_state.ip == 0) {

tests/basics/gen_stack_overflow.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def gen():
2+
yield from gen()
3+
4+
try:
5+
print(list(gen()))
6+
except RuntimeError:
7+
print("RuntimeError")

tests/run-tests

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ def run_tests(pyb, tests, args, base_path=".", num_threads=1):
345345
# Remove them from the below when they work
346346
if args.emit == 'native':
347347
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send'.split()}) # require yield
348-
skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join'.split()}) # require yield
348+
skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join gen_stack_overflow'.split()}) # require yield
349349
skip_tests.update({'basics/async_%s.py' % t for t in 'def await await2 for for2 with with2'.split()}) # require yield
350350
skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs
351351
skip_tests.update({'basics/%s.py' % t for t in 'with_break with_continue with_return'.split()}) # require complete with support

0 commit comments

Comments
 (0)