Avoid stack overflow in js_EmitTree on long left-associative operator chains (98901, r=jband, sr=shaver).

This commit is contained in:
brendan%mozilla.org 2001-10-27 18:38:16 +00:00
Родитель c96866b2d2
Коммит 467cafd25f
6 изменённых файлов: 108 добавлений и 63 удалений

Просмотреть файл

@ -2933,43 +2933,41 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
const char *bytes, size_t length)
{
jschar *chars;
JSScript *script;
void *mark;
JSBool result;
JSExceptionState *exnState;
void *tempMark;
JSTokenStream *ts;
JSErrorReporter older;
JSBool hitEOF, result;
JSExceptionState *exnState;
CHECK_REQUEST(cx);
mark = JS_ARENA_MARK(&cx->tempPool);
chars = js_InflateString(cx, bytes, length);
if (!chars)
return JS_TRUE;
/*
* Return true on any out-of-memory error, so our caller doesn't try to
* collect more buffered source.
*/
result = JS_TRUE;
exnState = JS_SaveExceptionState(cx);
tempMark = JS_ARENA_MARK(&cx->tempPool);
ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL);
if (!ts) {
result = JS_TRUE;
goto out;
if (ts) {
older = JS_SetErrorReporter(cx, NULL);
if (!js_ParseTokenStream(cx, obj, ts)) {
/*
* We ran into an error. If it was because we ran out of source,
* we return false, so our caller will know to try to collect more
* buffered source.
*/
result = (ts->flags & TSF_EOF) == 0;
}
JS_SetErrorReporter(cx, older);
js_CloseTokenStream(cx, ts);
JS_ARENA_RELEASE(&cx->tempPool, tempMark);
}
older = JS_SetErrorReporter(cx, NULL);
script = CompileTokenStream(cx, obj, ts, mark, &hitEOF);
JS_SetErrorReporter(cx, older);
if (script == NULL) {
/*
* We ran into an error, but it was because we ran out of source,
* and not for some other reason. For this case (and this case
* only) we return false, so the calling function knows to try to
* collect more source.
*/
result = hitEOF ? JS_FALSE : JS_TRUE;
} else {
result = JS_TRUE;
js_DestroyScript(cx, script);
}
out:
JS_free(cx, chars);
JS_RestoreExceptionState(cx, exnState);
return result;

Просмотреть файл

@ -3373,13 +3373,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case TOK_MINUS:
case TOK_STAR:
case TOK_DIVOP:
/* Binary operators that evaluate both operands unconditionally. */
if (!js_EmitTree(cx, cg, pn->pn_left))
return JS_FALSE;
if (!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
if (js_Emit1(cx, cg, pn->pn_op) < 0)
return JS_FALSE;
if (pn->pn_arity == PN_LIST) {
/* Left-associative operator chain: avoid too much recursion. */
pn2 = pn->pn_head;
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
op = pn->pn_op;
while ((pn2 = pn2->pn_next) != NULL) {
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
if (js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
}
} else {
/* Binary operators that evaluate both operands unconditionally. */
if (!js_EmitTree(cx, cg, pn->pn_left))
return JS_FALSE;
if (!js_EmitTree(cx, cg, pn->pn_right))
return JS_FALSE;
if (js_Emit1(cx, cg, pn->pn_op) < 0)
return JS_FALSE;
}
break;
#if JS_HAS_EXCEPTIONS

Просмотреть файл

@ -85,6 +85,7 @@ typedef enum JSOp {
#define JOF_ASSIGNING 0x2000 /* hint for JSClass.resolve, used for ops
that do simplex assignment */
#define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */
#define JOF_LEFTASSOC 0x8000 /* left-associative operator */
#define JOF_TYPE_IS_EXTENDED_JUMP(t) \
((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX))

Просмотреть файл

@ -85,23 +85,23 @@ OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 0, JOF_QVAR|J
OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE)
OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE)
OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 1, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING)
OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE)
OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE)
OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE)
OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE)
OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE)
OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE)
OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE)
OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE)
OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE)
OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE)
OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE)
OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE)
OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE)
OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 10, JOF_BYTE)
OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 10, JOF_BYTE)
OPDEF(JSOP_NEG, 34, "neg", "-", 1, 1, 1, 10, JOF_BYTE)
@ -213,8 +213,8 @@ OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 1, JOF_CONST|
OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE)
/* 'in' and 'instanceof' ops. */
OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE)
OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE)
OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE|JOF_LEFTASSOC)
/* debugger op */
OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE)

Просмотреть файл

@ -311,8 +311,6 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
JSTreeContext tc;
JSParseNode *pn;
JS_ASSERT(cx->runtime->gcDisabled);
/*
* Push a compiler frame if we have no frames, or if the top frame is a
* lightweight function activation, or if its scope chain doesn't match
@ -330,6 +328,13 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
cx->fp = &frame;
}
/*
* Prevent GC activation (possible if out of memory when atomizing,
* or from pre-ECMAv2 switch case expr eval in the unlikely case of a
* branch-callback -- unlikely because it means the switch case must
* have called a function).
*/
JS_DISABLE_GC(cx->runtime);
TREE_CONTEXT_INIT(&tc);
pn = Statements(cx, ts, &tc);
if (pn) {
@ -345,6 +350,7 @@ js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
}
TREE_CONTEXT_FINISH(&tc);
JS_ENABLE_GC(cx->runtime);
cx->fp = fp;
return pn;
}
@ -380,12 +386,7 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
cx->fp = &frame;
}
/*
* Prevent GC activation (possible if out of memory when atomizing,
* or from pre-ECMAv2 switch case expr eval in the unlikely case of a
* branch-callback -- unlikely because it means the switch case must
* have called a function).
*/
/* Prevent GC activation while compiling. */
JS_DISABLE_GC(cx->runtime);
pn = Statements(cx, ts, &cg->treeContext);
@ -3258,5 +3259,31 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
default:;
}
/*
* Flatten a left-associative (left-heavy) tree of a given operator into
* a list, to reduce js_EmitTree recursion.
*/
if (pn->pn_arity == PN_BINARY &&
pn1 &&
pn1->pn_type == pn->pn_type &&
pn1->pn_op == pn->pn_op &&
(js_CodeSpec[pn->pn_op].format & JOF_LEFTASSOC)) {
if (pn1->pn_arity == PN_LIST) {
/* We already flattened pn1, so move it to pn and append pn2. */
PN_MOVE_NODE(pn, pn1);
PN_APPEND(pn, pn2);
} else {
/* Convert pn into a list containing pn1's kids followed by pn2. */
pn->pn_arity = PN_LIST;
PN_INIT_LIST_1(pn, pn1->pn_left);
PN_APPEND(pn, pn1->pn_right);
PN_APPEND(pn, pn2);
/* Clear pn1 so it can be recycled by itself, without its kids. */
PN_CLEAR_NODE(pn1);
}
RecycleTree(pn1, tc);
}
return JS_TRUE;
}

Просмотреть файл

@ -249,9 +249,14 @@ struct JSParseNode {
(pn)->pn_op = (pn2)->pn_op; \
(pn)->pn_arity = (pn2)->pn_arity; \
(pn)->pn_u = (pn2)->pn_u; \
(pn2)->pn_type = TOK_EOF; \
(pn2)->pn_op = JSOP_NOP; \
(pn2)->pn_arity = PN_NULLARY; \
PN_CLEAR_NODE(pn2); \
JS_END_MACRO
#define PN_CLEAR_NODE(pn) \
JS_BEGIN_MACRO \
(pn)->pn_type = TOK_EOF; \
(pn)->pn_op = JSOP_NOP; \
(pn)->pn_arity = PN_NULLARY; \
JS_END_MACRO
/* True if pn is a parsenode representing a literal constant. */