diff -r d9c98730e2e8 Include/symtable.h --- a/Include/symtable.h Sat Jul 07 13:34:50 2012 +1000 +++ b/Include/symtable.h Sat Jul 07 14:39:38 2012 +0200 @@ -30,6 +30,8 @@ PyObject *st_private; /* name of current class or NULL */ PyFutureFeatures *st_future; /* module's future features that affect the symbol table */ + int recursion_depth; /* current recursion depth */ + int recursion_limit; /* recursion limit */ }; typedef struct _symtable_entry { diff -r d9c98730e2e8 Lib/test/test_compile.py --- a/Lib/test/test_compile.py Sat Jul 07 13:34:50 2012 +1000 +++ b/Lib/test/test_compile.py Sat Jul 07 14:39:38 2012 +0200 @@ -474,6 +474,14 @@ self.assertInvalidSingle('f()\nxy # blah\nblah()') self.assertInvalidSingle('x = 5 # comment\nx = 6\n') + def test_compiler_recursion_limit(self): + self.assertRaises(RuntimeError, self.compile_single, "()" * 100000) + self.assertRaises(RuntimeError, self.compile_single, "a" + ".b" * 100000) + self.assertRaises(RuntimeError, self.compile_single, "a" + "[0]" * 100000) + self.compile_single("()" * 2000) + self.compile_single("a" + ".b" * 2000) + self.compile_single("a" + "[0]" * 2000) + def test_main(): support.run_unittest(TestSpecifics) diff -r d9c98730e2e8 Python/symtable.c --- a/Python/symtable.c Sat Jul 07 13:34:50 2012 +1000 +++ b/Python/symtable.c Sat Jul 07 14:39:38 2012 +0200 @@ -218,17 +218,37 @@ return NULL; } +/* When compiling the use of C stack is probably going to be a lot + lighter than when executing Python code but still can overflow + and causing a Python crash if not checked (e.g. eval("()"*300000)). + Using the current recursion limit for the compiler too seems + overconstraining so a factor is used to allow deeper recursion + when compiling an expression. +*/ +#define COMPILER_STACK_FRAME_SCALE 4 + struct symtable * PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) { struct symtable *st = symtable_new(); asdl_seq *seq; int i; + PyThreadState *tstate; if (st == NULL) return st; st->st_filename = filename; st->st_future = future; + + /* Setup recursion depth check counters */ + tstate = PyThreadState_GET(); + if (!tstate) { + PySymtable_Free(st); + return NULL; + } + st->recursion_depth = tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE; + st->recursion_limit = Py_GetRecursionLimit() * COMPILER_STACK_FRAME_SCALE; + /* Make the initial symbol information gathering pass */ if (!GET_IDENTIFIER(top) || !symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0, 0)) { @@ -1268,6 +1288,12 @@ static int symtable_visit_expr(struct symtable *st, expr_ty e) { + if (++st->recursion_depth > st->recursion_limit) { + PyErr_SetString(PyExc_RuntimeError, + "maximum recursion depth exceeded while compiling an expression"); + --st->recursion_depth; + return 0; + } switch (e->kind) { case BoolOp_kind: VISIT_SEQ(st, expr, e->v.BoolOp.values); @@ -1280,8 +1306,10 @@ VISIT(st, expr, e->v.UnaryOp.operand); break; case Lambda_kind: { - if (!GET_IDENTIFIER(lambda)) + if (!GET_IDENTIFIER(lambda)) { + --st->recursion_depth; return 0; + } if (e->v.Lambda.args->defaults) VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); if (e->v.Lambda.args->kw_defaults) @@ -1289,12 +1317,16 @@ e->v.Lambda.args->kw_defaults); if (!symtable_enter_block(st, lambda, FunctionBlock, (void *)e, e->lineno, - e->col_offset)) + e->col_offset)) { + --st->recursion_depth; return 0; + } VISIT(st, arguments, e->v.Lambda.args); VISIT(st, expr, e->v.Lambda.body); - if (!symtable_exit_block(st, (void *)e)) + if (!symtable_exit_block(st, (void *)e)) { + --st->recursion_depth; return 0; + } break; } case IfExp_kind: @@ -1310,20 +1342,28 @@ VISIT_SEQ(st, expr, e->v.Set.elts); break; case GeneratorExp_kind: - if (!symtable_visit_genexp(st, e)) + if (!symtable_visit_genexp(st, e)) { + --st->recursion_depth; return 0; + } break; case ListComp_kind: - if (!symtable_visit_listcomp(st, e)) + if (!symtable_visit_listcomp(st, e)) { + --st->recursion_depth; return 0; + } break; case SetComp_kind: - if (!symtable_visit_setcomp(st, e)) + if (!symtable_visit_setcomp(st, e)) { + --st->recursion_depth; return 0; + } break; case DictComp_kind: - if (!symtable_visit_dictcomp(st, e)) + if (!symtable_visit_dictcomp(st, e)) { + --st->recursion_depth; return 0; + } break; case Yield_kind: case YieldFrom_kind: { @@ -1366,15 +1406,19 @@ break; case Name_kind: if (!symtable_add_def(st, e->v.Name.id, - e->v.Name.ctx == Load ? USE : DEF_LOCAL)) + e->v.Name.ctx == Load ? USE : DEF_LOCAL)) { + --st->recursion_depth; return 0; + } /* Special-case super: it counts as a use of __class__ */ if (e->v.Name.ctx == Load && st->st_cur->ste_type == FunctionBlock && !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) { if (!GET_IDENTIFIER(__class__) || - !symtable_add_def(st, __class__, USE)) + !symtable_add_def(st, __class__, USE)) { + --st->recursion_depth; return 0; + } } break; /* child nodes of List and Tuple will have expr_context set */ @@ -1385,6 +1429,7 @@ VISIT_SEQ(st, expr, e->v.Tuple.elts); break; } + --st->recursion_depth; return 1; }