ref: 5f83cc98423474d0c7a26b0494e84168621467d0
parent: 0365576bab685613d46a3f714b602b774c4e3e1c
author: Tor Andersson <[email protected]>
date: Tue May 16 09:05:50 EDT 2017
Add recursion overflow checks in parser. Add a recursion counter at every point we jump 'back' to in the grammar.
--- a/jsi.h
+++ b/jsi.h
@@ -72,6 +72,7 @@
#define JS_ENVLIMIT 64 /* environment stack size */
#define JS_TRYLIMIT 64 /* exception stack size */
#define JS_GCLIMIT 10000 /* run gc cycle every N allocations */
+#define JS_ASTLIMIT 100 /* max nested expressions */
/* instruction size -- change to int if you get integer overflow syntax errors */
typedef unsigned short js_Instruction;
@@ -168,6 +169,7 @@
int newline;
/* parser state */
+ int astdepth;
int astline;
int lookahead;
const char *text;
--- a/jsparse.c
+++ b/jsparse.c
@@ -23,6 +23,9 @@
JS_NORETURN static void jsP_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+#define INCREC() if (++J->astdepth > JS_ASTLIMIT) jsP_error(J, "too much recursion")
+#define DECREC() --J->astdepth
+
static void jsP_error(js_State *J, const char *fmt, ...)
{
va_list ap;
@@ -380,10 +383,13 @@
static js_Ast *memberexp(js_State *J)
{
- js_Ast *a = newexp(J);
+ js_Ast *a;
+ INCREC();
+ a = newexp(J);
loop:
if (jsP_accept(J, '.')) { a = EXP2(MEMBER, a, identifiername(J)); goto loop; }
if (jsP_accept(J, '[')) { a = EXP2(INDEX, a, expression(J, 0)); jsP_expect(J, ']'); goto loop; }
+ DECREC();
return a;
}
@@ -527,27 +533,33 @@
static js_Ast *assignment(js_State *J, int notin)
{
- js_Ast *a = conditional(J, notin);
- if (jsP_accept(J, '=')) return EXP2(ASS, a, assignment(J, notin));
- if (jsP_accept(J, TK_MUL_ASS)) return EXP2(ASS_MUL, a, assignment(J, notin));
- if (jsP_accept(J, TK_DIV_ASS)) return EXP2(ASS_DIV, a, assignment(J, notin));
- if (jsP_accept(J, TK_MOD_ASS)) return EXP2(ASS_MOD, a, assignment(J, notin));
- if (jsP_accept(J, TK_ADD_ASS)) return EXP2(ASS_ADD, a, assignment(J, notin));
- if (jsP_accept(J, TK_SUB_ASS)) return EXP2(ASS_SUB, a, assignment(J, notin));
- if (jsP_accept(J, TK_SHL_ASS)) return EXP2(ASS_SHL, a, assignment(J, notin));
- if (jsP_accept(J, TK_SHR_ASS)) return EXP2(ASS_SHR, a, assignment(J, notin));
- if (jsP_accept(J, TK_USHR_ASS)) return EXP2(ASS_USHR, a, assignment(J, notin));
- if (jsP_accept(J, TK_AND_ASS)) return EXP2(ASS_BITAND, a, assignment(J, notin));
- if (jsP_accept(J, TK_XOR_ASS)) return EXP2(ASS_BITXOR, a, assignment(J, notin));
- if (jsP_accept(J, TK_OR_ASS)) return EXP2(ASS_BITOR, a, assignment(J, notin));
+ js_Ast *a;
+ INCREC();
+ a = conditional(J, notin);
+ if (jsP_accept(J, '=')) a = EXP2(ASS, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_MUL_ASS)) a = EXP2(ASS_MUL, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_DIV_ASS)) a = EXP2(ASS_DIV, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_MOD_ASS)) a = EXP2(ASS_MOD, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_ADD_ASS)) a = EXP2(ASS_ADD, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_SUB_ASS)) a = EXP2(ASS_SUB, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_SHL_ASS)) a = EXP2(ASS_SHL, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_SHR_ASS)) a = EXP2(ASS_SHR, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_USHR_ASS)) a = EXP2(ASS_USHR, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_AND_ASS)) a = EXP2(ASS_BITAND, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_XOR_ASS)) a = EXP2(ASS_BITXOR, a, assignment(J, notin));
+ else if (jsP_accept(J, TK_OR_ASS)) a = EXP2(ASS_BITOR, a, assignment(J, notin));
+ DECREC();
return a;
}
static js_Ast *expression(js_State *J, int notin)
{
- js_Ast *a = assignment(J, notin);
+ js_Ast *a;
+ INCREC();
+ a = assignment(J, notin);
while (jsP_accept(J, ','))
a = EXP2(COMMA, a, assignment(J, notin));
+ DECREC();
return a;
}
@@ -673,23 +685,26 @@
static js_Ast *statement(js_State *J)
{
js_Ast *a, *b, *c, *d;
+ js_Ast *stm;
+ INCREC();
+
if (J->lookahead == '{') {
- return block(J);
+ stm = block(J);
}
- if (jsP_accept(J, TK_VAR)) {
+ else if (jsP_accept(J, TK_VAR)) {
a = vardeclist(J, 0);
semicolon(J);
- return STM1(VAR, a);
+ stm = STM1(VAR, a);
}
/* empty statement */
- if (jsP_accept(J, ';')) {
- return STM0(EMPTY);
+ else if (jsP_accept(J, ';')) {
+ stm = STM0(EMPTY);
}
- if (jsP_accept(J, TK_IF)) {
+ else if (jsP_accept(J, TK_IF)) {
jsP_expect(J, '(');
a = expression(J, 0);
jsP_expect(J, ')');
@@ -698,10 +713,10 @@
c = statement(J);
else
c = NULL;
- return STM3(IF, a, b, c);
+ stm = STM3(IF, a, b, c);
}
- if (jsP_accept(J, TK_DO)) {
+ else if (jsP_accept(J, TK_DO)) {
a = statement(J);
jsP_expect(J, TK_WHILE);
jsP_expect(J, '(');
@@ -708,51 +723,51 @@
b = expression(J, 0);
jsP_expect(J, ')');
semicolon(J);
- return STM2(DO, a, b);
+ stm = STM2(DO, a, b);
}
- if (jsP_accept(J, TK_WHILE)) {
+ else if (jsP_accept(J, TK_WHILE)) {
jsP_expect(J, '(');
a = expression(J, 0);
jsP_expect(J, ')');
b = statement(J);
- return STM2(WHILE, a, b);
+ stm = STM2(WHILE, a, b);
}
- if (jsP_accept(J, TK_FOR)) {
- return forstatement(J);
+ else if (jsP_accept(J, TK_FOR)) {
+ stm = forstatement(J);
}
- if (jsP_accept(J, TK_CONTINUE)) {
+ else if (jsP_accept(J, TK_CONTINUE)) {
a = identifieropt(J);
semicolon(J);
- return STM1(CONTINUE, a);
+ stm = STM1(CONTINUE, a);
}
- if (jsP_accept(J, TK_BREAK)) {
+ else if (jsP_accept(J, TK_BREAK)) {
a = identifieropt(J);
semicolon(J);
- return STM1(BREAK, a);
+ stm = STM1(BREAK, a);
}
- if (jsP_accept(J, TK_RETURN)) {
+ else if (jsP_accept(J, TK_RETURN)) {
if (J->lookahead != ';' && J->lookahead != '}' && J->lookahead != 0)
a = expression(J, 0);
else
a = NULL;
semicolon(J);
- return STM1(RETURN, a);
+ stm = STM1(RETURN, a);
}
- if (jsP_accept(J, TK_WITH)) {
+ else if (jsP_accept(J, TK_WITH)) {
jsP_expect(J, '(');
a = expression(J, 0);
jsP_expect(J, ')');
b = statement(J);
- return STM2(WITH, a, b);
+ stm = STM2(WITH, a, b);
}
- if (jsP_accept(J, TK_SWITCH)) {
+ else if (jsP_accept(J, TK_SWITCH)) {
jsP_expect(J, '(');
a = expression(J, 0);
jsP_expect(J, ')');
@@ -759,16 +774,16 @@
jsP_expect(J, '{');
b = caselist(J);
jsP_expect(J, '}');
- return STM2(SWITCH, a, b);
+ stm = STM2(SWITCH, a, b);
}
- if (jsP_accept(J, TK_THROW)) {
+ else if (jsP_accept(J, TK_THROW)) {
a = expression(J, 0);
semicolon(J);
- return STM1(THROW, a);
+ stm = STM1(THROW, a);
}
- if (jsP_accept(J, TK_TRY)) {
+ else if (jsP_accept(J, TK_TRY)) {
a = block(J);
b = c = d = NULL;
if (jsP_accept(J, TK_CATCH)) {
@@ -782,35 +797,40 @@
}
if (!b && !d)
jsP_error(J, "unexpected token in try: %s (expected 'catch' or 'finally')", jsY_tokenstring(J->lookahead));
- return STM4(TRY, a, b, c, d);
+ stm = STM4(TRY, a, b, c, d);
}
- if (jsP_accept(J, TK_DEBUGGER)) {
+ else if (jsP_accept(J, TK_DEBUGGER)) {
semicolon(J);
- return STM0(DEBUGGER);
+ stm = STM0(DEBUGGER);
}
- if (jsP_accept(J, TK_FUNCTION)) {
+ else if (jsP_accept(J, TK_FUNCTION)) {
jsP_warning(J, "function statements are not standard");
- return funstm(J);
+ stm = funstm(J);
}
/* labelled statement or expression statement */
- if (J->lookahead == TK_IDENTIFIER) {
+ else if (J->lookahead == TK_IDENTIFIER) {
a = expression(J, 0);
if (a->type == EXP_IDENTIFIER && jsP_accept(J, ':')) {
a->type = AST_IDENTIFIER;
b = statement(J);
- return STM2(LABEL, a, b);
+ stm = STM2(LABEL, a, b);
+ } else {
+ semicolon(J);
+ stm = a;
}
- semicolon(J);
- return a;
}
/* expression statement */
- a = expression(J, 0);
- semicolon(J);
- return a;
+ else {
+ stm = expression(J, 0);
+ semicolon(J);
+ }
+
+ DECREC();
+ return stm;
}
/* Program */
@@ -925,6 +945,7 @@
jsY_initlex(J, filename, source);
jsP_next(J);
+ J->astdepth = 0;
p = script(J, 0);
if (p)
jsP_foldconst(p);
@@ -938,6 +959,7 @@
if (params) {
jsY_initlex(J, filename, params);
jsP_next(J);
+ J->astdepth = 0;
p = parameters(J);
}
return EXP3(FUN, NULL, p, jsP_parse(J, filename, body));