summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2012-11-04 13:14:34 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2012-11-04 13:14:34 (GMT)
commitaab9c2b2ead7b786947c367a9cc5d9c921b9ea99 (patch)
tree323a662a1f65b39e90d28f9f259bc937467691a7 /Python
parent1321edaa55a8df94e739e6ea5e9e658f2b7bfcc2 (diff)
downloadcpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.zip
cpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.tar.gz
cpython-aab9c2b2ead7b786947c367a9cc5d9c921b9ea99.tar.bz2
Issue #5765: Apply a hard recursion limit in the compiler
Previously, excessive nesting in expressions would blow the stack and segfault the interpreter. Now, a hard limit based on the configured recursion limit and a hardcoded scaling factor is applied.
Diffstat (limited to 'Python')
-rw-r--r--Python/compile.c5
-rw-r--r--Python/symtable.c97
2 files changed, 73 insertions, 29 deletions
diff --git a/Python/compile.c b/Python/compile.c
index ff55220..5016f99 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -141,6 +141,11 @@ struct compiler_unit {
The u pointer points to the current compilation unit, while units
for enclosing blocks are stored in c_stack. The u and c_stack are
managed by compiler_enter_scope() and compiler_exit_scope().
+
+Note that we don't track recursion levels during compilation - the
+task of detecting and rejecting excessive levels of nesting is
+handled by the symbol analysis pass.
+
*/
struct compiler {
diff --git a/Python/symtable.c b/Python/symtable.c
index ff6e8b7..9dde908 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -220,17 +220,40 @@ symtable_new(void)
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 seems too
+ restrictive (it caused at least one test to fail) so a factor is
+ used to allow deeper recursion when compiling an expression.
+
+ Using a scaling factor means this should automatically adjust when
+ the recursion limit is adjusted for small or large C stack allocations.
+*/
+#define COMPILER_STACK_FRAME_SCALE 3
+
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)) {
@@ -1013,11 +1036,17 @@ error:
VISIT_SEQ_TAIL permits the start of an ASDL sequence to be skipped, which is
useful if the first node in the sequence requires special treatment.
+
+ VISIT_QUIT macro returns the specified value exiting from the function but
+ first adjusts current recursion counter depth.
*/
+#define VISIT_QUIT(ST, X) \
+ return --(ST)->recursion_depth,(X)
+
#define VISIT(ST, TYPE, V) \
if (!symtable_visit_ ## TYPE((ST), (V))) \
- return 0;
+ VISIT_QUIT((ST), 0);
#define VISIT_SEQ(ST, TYPE, SEQ) { \
int i; \
@@ -1025,7 +1054,7 @@ error:
for (i = 0; i < asdl_seq_LEN(seq); i++) { \
TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \
if (!symtable_visit_ ## TYPE((ST), elt)) \
- return 0; \
+ VISIT_QUIT((ST), 0); \
} \
}
@@ -1035,7 +1064,7 @@ error:
for (i = (START); i < asdl_seq_LEN(seq); i++) { \
TYPE ## _ty elt = (TYPE ## _ty)asdl_seq_GET(seq, i); \
if (!symtable_visit_ ## TYPE((ST), elt)) \
- return 0; \
+ VISIT_QUIT((ST), 0); \
} \
}
@@ -1046,7 +1075,7 @@ error:
expr_ty elt = (expr_ty)asdl_seq_GET(seq, i); \
if (!elt) continue; /* can be NULL */ \
if (!symtable_visit_expr((ST), elt)) \
- return 0; \
+ VISIT_QUIT((ST), 0); \
} \
}
@@ -1071,32 +1100,37 @@ symtable_new_tmpname(struct symtable *st)
static int
symtable_visit_stmt(struct symtable *st, stmt_ty s)
{
+ if (++st->recursion_depth > st->recursion_limit) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "maximum recursion depth exceeded during compilation");
+ VISIT_QUIT(st, 0);
+ }
switch (s->kind) {
case FunctionDef_kind:
if (!symtable_add_def(st, s->v.FunctionDef.name, DEF_LOCAL))
- return 0;
+ VISIT_QUIT(st, 0);
if (s->v.FunctionDef.args->defaults)
VISIT_SEQ(st, expr, s->v.FunctionDef.args->defaults);
if (s->v.FunctionDef.args->kw_defaults)
VISIT_KWONLYDEFAULTS(st,
s->v.FunctionDef.args->kw_defaults);
if (!symtable_visit_annotations(st, s))
- return 0;
+ VISIT_QUIT(st, 0);
if (s->v.FunctionDef.decorator_list)
VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list);
if (!symtable_enter_block(st, s->v.FunctionDef.name,
FunctionBlock, (void *)s, s->lineno,
s->col_offset))
- return 0;
+ VISIT_QUIT(st, 0);
VISIT(st, arguments, s->v.FunctionDef.args);
VISIT_SEQ(st, stmt, s->v.FunctionDef.body);
if (!symtable_exit_block(st, s))
- return 0;
+ VISIT_QUIT(st, 0);
break;
case ClassDef_kind: {
PyObject *tmp;
if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL))
- return 0;
+ VISIT_QUIT(st, 0);
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
if (s->v.ClassDef.starargs)
@@ -1107,20 +1141,20 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list);
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
(void *)s, s->lineno, s->col_offset))
- return 0;
+ VISIT_QUIT(st, 0);
if (!GET_IDENTIFIER(__class__) ||
!symtable_add_def(st, __class__, DEF_LOCAL) ||
!GET_IDENTIFIER(__locals__) ||
!symtable_add_def(st, __locals__, DEF_PARAM)) {
symtable_exit_block(st, s);
- return 0;
+ VISIT_QUIT(st, 0);
}
tmp = st->st_private;
st->st_private = s->v.ClassDef.name;
VISIT_SEQ(st, stmt, s->v.ClassDef.body);
st->st_private = tmp;
if (!symtable_exit_block(st, s))
- return 0;
+ VISIT_QUIT(st, 0);
break;
}
case Return_kind:
@@ -1204,7 +1238,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
identifier name = (identifier)asdl_seq_GET(seq, i);
long cur = symtable_lookup(st, name);
if (cur < 0)
- return 0;
+ VISIT_QUIT(st, 0);
if (cur & (DEF_LOCAL | USE)) {
char buf[256];
char *c_name = _PyUnicode_AsString(name);
@@ -1219,10 +1253,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
GLOBAL_AFTER_USE,
c_name);
if (!symtable_warn(st, buf, s->lineno))
- return 0;
+ VISIT_QUIT(st, 0);
}
if (!symtable_add_def(st, name, DEF_GLOBAL))
- return 0;
+ VISIT_QUIT(st, 0);
}
break;
}
@@ -1233,7 +1267,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
identifier name = (identifier)asdl_seq_GET(seq, i);
long cur = symtable_lookup(st, name);
if (cur < 0)
- return 0;
+ VISIT_QUIT(st, 0);
if (cur & (DEF_LOCAL | USE)) {
char buf[256];
char *c_name = _PyUnicode_AsString(name);
@@ -1248,10 +1282,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
NONLOCAL_AFTER_USE,
c_name);
if (!symtable_warn(st, buf, s->lineno))
- return 0;
+ VISIT_QUIT(st, 0);
}
if (!symtable_add_def(st, name, DEF_NONLOCAL))
- return 0;
+ VISIT_QUIT(st, 0);
}
break;
}
@@ -1268,12 +1302,17 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
VISIT_SEQ(st, stmt, s->v.With.body);
break;
}
- return 1;
+ VISIT_QUIT(st, 1);
}
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 during compilation");
+ VISIT_QUIT(st, 0);
+ }
switch (e->kind) {
case BoolOp_kind:
VISIT_SEQ(st, expr, e->v.BoolOp.values);
@@ -1287,7 +1326,7 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
break;
case Lambda_kind: {
if (!GET_IDENTIFIER(lambda))
- return 0;
+ VISIT_QUIT(st, 0);
if (e->v.Lambda.args->defaults)
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
if (e->v.Lambda.args->kw_defaults)
@@ -1296,11 +1335,11 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
if (!symtable_enter_block(st, lambda,
FunctionBlock, (void *)e, e->lineno,
e->col_offset))
- return 0;
+ VISIT_QUIT(st, 0);
VISIT(st, arguments, e->v.Lambda.args);
VISIT(st, expr, e->v.Lambda.body);
if (!symtable_exit_block(st, (void *)e))
- return 0;
+ VISIT_QUIT(st, 0);
break;
}
case IfExp_kind:
@@ -1317,19 +1356,19 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
break;
case GeneratorExp_kind:
if (!symtable_visit_genexp(st, e))
- return 0;
+ VISIT_QUIT(st, 0);
break;
case ListComp_kind:
if (!symtable_visit_listcomp(st, e))
- return 0;
+ VISIT_QUIT(st, 0);
break;
case SetComp_kind:
if (!symtable_visit_setcomp(st, e))
- return 0;
+ VISIT_QUIT(st, 0);
break;
case DictComp_kind:
if (!symtable_visit_dictcomp(st, e))
- return 0;
+ VISIT_QUIT(st, 0);
break;
case Yield_kind:
case YieldFrom_kind: {
@@ -1373,14 +1412,14 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
case Name_kind:
if (!symtable_add_def(st, e->v.Name.id,
e->v.Name.ctx == Load ? USE : DEF_LOCAL))
- return 0;
+ VISIT_QUIT(st, 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))
- return 0;
+ VISIT_QUIT(st, 0);
}
break;
/* child nodes of List and Tuple will have expr_context set */
@@ -1391,7 +1430,7 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT_SEQ(st, expr, e->v.Tuple.elts);
break;
}
- return 1;
+ VISIT_QUIT(st, 1);
}
static int