зеркало из https://github.com/mozilla/gecko-dev.git
Avoid stack overflow in js_EmitTree on long left-associative operator chains (98901, r=jband, sr=shaver).
This commit is contained in:
Родитель
c96866b2d2
Коммит
467cafd25f
|
@ -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. */
|
||||
|
|
Загрузка…
Ссылка в новой задаче