Bug 667915 - Don't let content JS consume all the stack and cause chrome JS to OOM (r=waldo,mrbkap)

This commit is contained in:
Luke Wagner 2011-06-30 09:26:56 -07:00
Родитель d96139acde
Коммит 5a5ed0ab29
32 изменённых файлов: 516 добавлений и 322 удалений

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

@ -59,7 +59,7 @@ public:
NS_DECL_NSIPRINCIPAL
NS_DECL_NSISERIALIZABLE
nsresult Init();
nsresult Init(JSPrincipals **jsprin);
nsSystemPrincipal();

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

@ -3379,7 +3379,8 @@ nsresult nsScriptSecurityManager::Init()
nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
rv = system->Init();
JSPrincipals *jsprin;
rv = system->Init(&jsprin);
NS_ENSURE_SUCCESS(rv, rv);
mSystemPrincipal = system;
@ -3406,6 +3407,8 @@ nsresult nsScriptSecurityManager::Init()
JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
JS_SetTrustedPrincipals(sRuntime, jsprin);
return NS_OK;
}
@ -3429,6 +3432,7 @@ nsScriptSecurityManager::Shutdown()
{
if (sRuntime) {
JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
JS_SetTrustedPrincipals(sRuntime, NULL);
sRuntime = nsnull;
}
sEnabledID = JSID_VOID;

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

@ -314,7 +314,7 @@ nsSystemPrincipal::nsSystemPrincipal()
#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
nsresult
nsSystemPrincipal::Init()
nsSystemPrincipal::Init(JSPrincipals **jsprin)
{
// Use an nsCString so we only do the allocation once here and then
// share with nsJSPrincipals
@ -324,7 +324,11 @@ nsSystemPrincipal::Init()
return NS_ERROR_OUT_OF_MEMORY;
}
return mJSPrincipals.Init(this, str);
nsresult rv = mJSPrincipals.Init(this, str);
NS_ENSURE_SUCCESS(rv, rv);
*jsprin = &mJSPrincipals;
return NS_OK;
}
nsSystemPrincipal::~nsSystemPrincipal(void)

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

@ -0,0 +1,12 @@
foo = evalcx("(function foo() { foo.bar() })");
foo.bar = evalcx("(function bar() {})");
function fatty() {
try {
fatty();
} catch (e) {
foo();
}
}
fatty();

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

@ -0,0 +1,15 @@
function foo() {
bar(1,2,3,4,5,6,7,8,9);
}
function bar() {
foo(1,2,3,4,5,6,7,8,9);
}
var caught = false;
try {
foo();
} catch (e) {
caught = true;
}
assertEq(caught, true);

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

@ -86,6 +86,7 @@ CPPSRCS = \
testXDR.cpp \
testCustomIterator.cpp \
testExternalStrings.cpp \
testChromeBuffer.cpp \
$(NULL)
# Disabled: an entirely unrelated test seems to cause this to fail. Moreover,

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

@ -0,0 +1,161 @@
#include "tests.h"
JSPrincipals system_principals = {
NULL, NULL, NULL, 1, NULL, NULL
};
JSClass global_class = {
"global",
JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub,
JS_PropertyStub,
JS_PropertyStub,
JS_StrictPropertyStub,
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JS::Anchor<JSObject *> trusted_glob, trusted_fun;
JSBool
CallTrusted(JSContext *cx, uintN argc, jsval *vp)
{
if (!JS_SaveFrameChain(cx))
return JS_FALSE;
JSBool ok = JS_FALSE;
{
JSAutoEnterCompartment ac;
ok = ac.enter(cx, trusted_glob.get());
if (!ok)
goto out;
ok = JS_CallFunctionValue(cx, NULL, OBJECT_TO_JSVAL(trusted_fun.get()),
0, NULL, vp);
}
out:
JS_RestoreFrameChain(cx);
return ok;
}
BEGIN_TEST(testChromeBuffer)
{
JS_SetTrustedPrincipals(rt, &system_principals);
JSFunction *fun;
JSObject *o;
CHECK(o = JS_NewCompartmentAndGlobalObject(cx, &global_class, &system_principals));
trusted_glob.set(o);
/*
* Check that, even after untrusted content has exhausted the stack, code
* compiled with "trusted principals" can run using reserved trusted-only
* buffer space.
*/
{
{
JSAutoEnterCompartment ac;
CHECK(ac.enter(cx, trusted_glob.get()));
const char *paramName = "x";
const char *bytes = "return x ? 1 + trusted(x-1) : 0";
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
"trusted", 1, &paramName, bytes, strlen(bytes),
"", 0));
trusted_fun.set(JS_GetFunctionObject(fun));
}
jsval v = OBJECT_TO_JSVAL(trusted_fun.get());
CHECK(JS_WrapValue(cx, &v));
const char *paramName = "trusted";
const char *bytes = "try { "
" return untrusted(trusted); "
"} catch (e) { "
" return trusted(100); "
"} ";
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
bytes, strlen(bytes), "", 0));
jsval rval;
CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval));
CHECK(JSVAL_TO_INT(rval) == 100);
}
/*
* Check that content called from chrome in the reserved-buffer space
* immediately ooms.
*/
{
{
JSAutoEnterCompartment ac;
CHECK(ac.enter(cx, trusted_glob.get()));
const char *paramName = "untrusted";
const char *bytes = "try { "
" untrusted(); "
"} catch (e) { "
" return 'From trusted: ' + e; "
"} ";
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
"trusted", 1, &paramName, bytes, strlen(bytes),
"", 0));
trusted_fun.set(JS_GetFunctionObject(fun));
}
jsval v = OBJECT_TO_JSVAL(trusted_fun.get());
CHECK(JS_WrapValue(cx, &v));
const char *paramName = "trusted";
const char *bytes = "try { "
" return untrusted(trusted); "
"} catch (e) { "
" return trusted(untrusted); "
"} ";
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
bytes, strlen(bytes), "", 0));
jsval rval;
CHECK(JS_CallFunction(cx, NULL, fun, 1, &v, &rval));
JSBool match;
CHECK(JS_StringEqualsAscii(cx, JSVAL_TO_STRING(rval), "From trusted: InternalError: too much recursion", &match));
CHECK(match);
}
/*
* Check that JS_SaveFrameChain called on the way from content to chrome
* (say, as done by XPCJSContextSTack::Push) works.
*/
{
{
JSAutoEnterCompartment ac;
CHECK(ac.enter(cx, trusted_glob.get()));
const char *bytes = "return 42";
CHECK(fun = JS_CompileFunctionForPrincipals(cx, trusted_glob.get(), &system_principals,
"trusted", 0, NULL, bytes, strlen(bytes),
"", 0));
trusted_fun.set(JS_GetFunctionObject(fun));
}
JSFunction *fun = JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted");
JS::Anchor<JSObject *> callTrusted(JS_GetFunctionObject(fun));
const char *paramName = "f";
const char *bytes = "try { "
" return untrusted(trusted); "
"} catch (e) { "
" return f(); "
"} ";
CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
bytes, strlen(bytes), "", 0));
jsval arg = OBJECT_TO_JSVAL(callTrusted.get());
jsval rval;
CHECK(JS_CallFunction(cx, NULL, fun, 1, &arg, &rval));
CHECK(JSVAL_TO_INT(rval) == 42);
}
return true;
}
END_TEST(testChromeBuffer)

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

@ -637,7 +637,8 @@ static JSBool js_NewRuntimeWasCalled = JS_FALSE;
#endif
JSRuntime::JSRuntime()
: gcChunkAllocator(&defaultGCChunkAllocator)
: gcChunkAllocator(&defaultGCChunkAllocator),
trustedPrincipals_(NULL)
{
/* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
JS_INIT_CLIST(&contextList);
@ -4152,6 +4153,12 @@ JS_GetSecurityCallbacks(JSContext *cx)
: cx->runtime->securityCallbacks;
}
JS_PUBLIC_API(void)
JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin)
{
rt->setTrustedPrincipals(prin);
}
JS_PUBLIC_API(JSFunction *)
JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags,
JSObject *parent, const char *name)

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

@ -2594,6 +2594,21 @@ JS_SetContextSecurityCallbacks(JSContext *cx, JSSecurityCallbacks *callbacks);
extern JS_PUBLIC_API(JSSecurityCallbacks *)
JS_GetSecurityCallbacks(JSContext *cx);
/*
* Code running with "trusted" principals will be given a deeper stack
* allocation than ordinary scripts. This allows trusted script to run after
* untrusted script has exhausted the stack. This function sets the
* runtime-wide trusted principal.
*
* This principals is not held (via JS_HoldPrincipals/JS_DropPrincipals) since
* there is no available JSContext. Instead, the caller must ensure that the
* given principals stays valid for as long as 'rt' may point to it. If the
* principals would be destroyed before 'rt', JS_SetTrustedPrincipals must be
* called again, passing NULL for 'prin'.
*/
extern JS_PUBLIC_API(void)
JS_SetTrustedPrincipals(JSRuntime *rt, JSPrincipals *prin);
/************************************************************************/
/*

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

@ -597,6 +597,12 @@ struct JSRuntime {
#define JS_THREAD_DATA(cx) (&(cx)->runtime->threadData)
#endif
private:
JSPrincipals *trustedPrincipals_;
public:
void setTrustedPrincipals(JSPrincipals *p) { trustedPrincipals_ = p; }
JSPrincipals *trustedPrincipals() const { return trustedPrincipals_; }
/*
* Object shape (property cache structural type) identifier generator.
*

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

@ -186,7 +186,7 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
js::ArgumentsObject *
ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee)
{
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
JSObject *proto;
if (!js_GetClassPrototype(cx, callee.getGlobal(), JSProto_Object, &proto))
@ -2119,7 +2119,7 @@ js_fun_apply(JSContext *cx, uintN argc, Value *vp)
LeaveTrace(cx);
/* Step 6. */
uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
uintN n = uintN(JS_MIN(length, StackSpace::ARGS_LENGTH_MAX));
InvokeArgsGuard args;
if (!cx->stack.pushInvokeArgs(cx, n, &args))
@ -2223,7 +2223,7 @@ CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
uintN argslen;
const Value *boundArgs = obj->getBoundFunctionArguments(argslen);
if (argc + argslen > JS_ARGS_LENGTH_MAX) {
if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) {
js_ReportAllocationOverflow(cx);
return false;
}

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

@ -576,25 +576,6 @@ js_PutArgsObject(js::StackFrame *fp);
inline bool
js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
/*
* Maximum supported value of arguments.length. It bounds the maximum number of
* arguments that can be supplied via the second (so-called |argArray|) param
* to Function.prototype.apply. This value also bounds the number of elements
* parsed in an array initialiser.
*
* The thread's stack is the limiting factor for this number. It is currently
* 2MB, which fits a little less than 2^19 arguments (once the stack frame,
* callstack, etc. are included). Pick a max args length that is a little less.
*/
const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(19) - 1024;
/*
* JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as an Int32
* Value. Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must be less than JSVAL_INT_MAX.
*/
JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
JS_STATIC_ASSERT(((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
extern JSBool
js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);

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

@ -625,7 +625,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
/* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
CallArgs args = argsRef;
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(args.argc() <= StackSpace::ARGS_LENGTH_MAX);
if (args.calleev().isPrimitive()) {
js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(construct));
@ -746,7 +746,7 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
JS_CHECK_RECURSION(cx, return false);
stackLimit_ = stack.space().getStackLimit(cx);
stackLimit_ = stack.space().getStackLimit(cx, REPORT_ERROR);
if (!stackLimit_)
return false;
@ -4095,7 +4095,7 @@ BEGIN_CASE(JSOP_FUNAPPLY)
}
JSScript *newScript = fun->script();
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, construct, OOMCheck()))
if (!cx->stack.pushInlineFrame(cx, regs, args, *callee, fun, newScript, construct))
goto error;
/* Refresh local js::Interpret state. */
@ -5310,7 +5310,7 @@ BEGIN_CASE(JSOP_INITELEM)
if (rref.isMagic(JS_ARRAY_HOLE)) {
JS_ASSERT(obj->isArray());
JS_ASSERT(JSID_IS_INT(id));
JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
!js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
goto error;

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

@ -8302,7 +8302,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
matched = tokenStream.matchToken(TOK_RB, TSF_OPERAND);
if (!matched) {
for (index = 0; ; index++) {
if (index == JS_ARGS_LENGTH_MAX) {
if (index == StackSpace::ARGS_LENGTH_MAX) {
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_ARRAY_INIT_TOO_BIG);
return NULL;
}

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

@ -55,7 +55,6 @@
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"

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

@ -61,7 +61,6 @@
#include "jsbool.h"
#include "jsbuiltins.h"
#include "jscntxt.h"
#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */
#include "jsgc.h"
#include "jsinterp.h"
#include "jslock.h"
@ -3101,7 +3100,7 @@ static JSBool
str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
{
Value *argv = JS_ARGV(cx, vp);
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
if (argc == 1) {
uint16_t code;
if (!ValueToUint16(cx, argv[0], &code))

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

@ -5739,8 +5739,7 @@ SynthesizeFrame(JSContext* cx, const FrameInfo& fi, JSObject* callee)
/* Push a frame for the call. */
CallArgs args = CallArgsFromSp(fi.get_argc(), regs.sp);
cx->stack.pushInlineFrame(cx, regs, args, *callee, newfun, newscript,
MaybeConstructFromBool(fi.is_constructing()),
NoCheck());
MaybeConstructFromBool(fi.is_constructing()));
#ifdef DEBUG
/* These should be initialized by FlushNativeStackFrame. */
@ -6635,7 +6634,8 @@ ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
#endif
JS_ASSERT(f->root == f && f->code());
if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace()) {
if (!ScopeChainCheck(cx, f) ||
!cx->stack.space().ensureEnoughSpaceToEnterTrace(cx)) {
*lrp = NULL;
return true;
}

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

@ -670,6 +670,16 @@ PodEqual(T *one, T *two, size_t len)
return !memcmp(one, two, len * sizeof(T));
}
/*
* Ordinarily, a function taking a JSContext* 'cx' paremter reports errors on
* the context. In some cases, functions optionally report and indicate this by
* taking a nullable 'maybecx' parameter. In some cases, though, a function
* always needs a 'cx', but optionally reports. This option is presented by the
* MaybeReportError.
*/
enum MaybeReportError { REPORT_ERROR = true, DONT_REPORT_ERROR = false };
} /* namespace js */
#endif /* defined(__cplusplus) */

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

@ -399,7 +399,7 @@ ForceFrame::enter()
JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative());
return context->stack.pushDummyFrame(context, *scopeChain, frame);
return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
}
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
@ -424,13 +424,23 @@ AutoCompartment::enter()
if (origin != destination) {
LeaveTrace(context);
context->compartment = destination;
JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative());
frame.construct();
if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref())) {
/*
* Set the compartment eagerly so that pushDummyFrame associates the
* resource allocation request with 'destination' instead of 'origin'.
* (This is important when content has overflowed the stack and chrome
* is preparing to run JS to throw up a slow script dialog.) However,
* if an exception is thrown, we need it to be in origin's compartment
* so be careful to only report after restoring.
*/
context->compartment = destination;
if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
context->compartment = origin;
js_ReportOverRecursed(context);
return false;
}

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

@ -202,9 +202,8 @@ void JS_FASTCALL
stubs::HitStackQuota(VMFrame &f)
{
/* Include space to push another frame. */
uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
JS_ASSERT(f.regs.sp == f.fp()->base());
if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
f.stackLimit = f.cx->stack.space().getStackLimit(f.cx, DONT_REPORT_ERROR);
if (f.stackLimit)
return;
f.cx->stack.popFrameAfterOverflow();
@ -240,14 +239,15 @@ stubs::FixupArity(VMFrame &f, uint32 nactual)
/* Reserve enough space for a callee frame. */
CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
StackFrame *fp = cx->stack.getFixupFrame(cx, f.regs, args, fun, script, ncode,
construct, LimitCheck(&f.stackLimit));
StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
script, ncode, construct, &f.stackLimit);
if (!fp) {
/*
* The PC is not coherent with the current frame, so fix it up for
* exception handling.
*/
f.regs.pc = f.jit()->nativeToPC(ncode);
js_ReportOverRecursed(cx);
THROWV(NULL);
}
@ -316,8 +316,7 @@ UncachedInlineCall(VMFrame &f, MaybeConstruct construct, void **pret, bool *unji
JSScript *newscript = newfun->script();
/* Get pointer to new frame/slots, prepare arguments. */
LimitCheck check(&f.stackLimit);
if (!cx->stack.pushInlineFrame(cx, f.regs, args, callee, newfun, newscript, construct, check))
if (!cx->stack.pushInlineFrame(cx, f.regs, args, callee, newfun, newscript, construct, &f.stackLimit))
return false;
/* Scope with a call object parented by callee's parent. */

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

@ -709,7 +709,7 @@ CheckStackAndEnterMethodJIT(JSContext *cx, StackFrame *fp, void *code)
{
JS_CHECK_RECURSION(cx, return false);
Value *stackLimit = cx->stack.space().getStackLimit(cx);
Value *stackLimit = cx->stack.space().getStackLimit(cx, REPORT_ERROR);
if (!stackLimit)
return false;

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

@ -1096,7 +1096,7 @@ ic::SplatApplyArgs(VMFrame &f)
THROWV(false);
/* Step 6. */
n = Min(length, JS_ARGS_LENGTH_MAX);
n = Min(length, StackSpace::ARGS_LENGTH_MAX);
if (!BumpStack(f, n))
THROWV(false);
@ -1151,7 +1151,7 @@ ic::SplatApplyArgs(VMFrame &f)
JS_ASSERT(!JS_ON_TRACE(cx));
/* Step 6. */
uintN n = uintN(JS_MIN(length, JS_ARGS_LENGTH_MAX));
uintN n = uintN(JS_MIN(length, StackSpace::ARGS_LENGTH_MAX));
intN delta = n - 1;
if (delta > 0 && !BumpStack(f, delta))

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

@ -1299,7 +1299,7 @@ stubs::InitElem(VMFrame &f, uint32 last)
if (rref.isMagic(JS_ARRAY_HOLE)) {
JS_ASSERT(obj->isArray());
JS_ASSERT(JSID_IS_INT(id));
JS_ASSERT(jsuint(JSID_TO_INT(id)) < JS_ARGS_LENGTH_MAX);
JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1)))
THROW();
} else {

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

@ -4797,6 +4797,13 @@ EnableStackWalkingAssertion(JSContext *cx, uintN argc, jsval *vp)
return true;
}
static JSBool
GetMaxArgs(JSContext *cx, uintN arg, jsval *vp)
{
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX));
return JS_TRUE;
}
static JSFunctionSpec shell_functions[] = {
JS_FN("version", Version, 0,0),
JS_FN("revertVersion", RevertVersion, 0,0),
@ -4897,6 +4904,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("newGlobal", NewGlobal, 1,0),
JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0),
JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0),
JS_FN("getMaxArgs", GetMaxArgs, 0,0),
JS_FS_END
};
@ -5042,6 +5050,7 @@ static const char *const shell_help_messages[] = {
" code. If your test isn't ridiculously thorough, such that performing this\n"
" assertion increases test duration by an order of magnitude, you shouldn't\n"
" use this.",
"getMaxArgs() Return the maximum number of supported args for a call."
/* Keep these last: see the static assertion below. */
#ifdef MOZ_PROFILING
@ -6046,6 +6055,21 @@ MaybeOverrideOutFileFromEnv(const char* const envVar,
}
}
JSBool
ShellPrincipalsSubsume(JSPrincipals *, JSPrincipals *)
{
return JS_TRUE;
}
JSPrincipals shellTrustedPrincipals = {
(char *)"[shell trusted principals]",
NULL,
NULL,
1,
NULL, /* nobody should be destroying this */
ShellPrincipalsSubsume
};
int
main(int argc, char **argv, char **envp)
{
@ -6132,6 +6156,8 @@ main(int argc, char **argv, char **envp)
if (!rt)
return 1;
JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals);
if (!InitWatchdog(rt))
return 1;

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

@ -19,7 +19,6 @@ script regress-345961.js
script regress-348810.js
script regress-350256-01.js
script regress-350256-02.js
silentfail script regress-350256-03.js
script regress-360681-01.js
script regress-360681-02.js
script regress-364104.js

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

@ -37,7 +37,7 @@
//-----------------------------------------------------------------------------
var BUGNUMBER = 350256;
var summary = 'Array.apply maximum arguments: 2^16';
var summary = 'Array.apply maximum arguments';
var actual = '';
var expect = '';

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

@ -37,13 +37,14 @@
//-----------------------------------------------------------------------------
var BUGNUMBER = 350256;
var summary = 'Array.apply maximum arguments: 2^19 - 1024';
var summary = 'Array.apply maximum arguments';
var actual = '';
var expect = '';
//-----------------------------------------------------------------------------
test(Math.pow(2, 19) - 1024);
if (getMaxArgs)
test(getMaxArgs());
//-----------------------------------------------------------------------------
function test(length)

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

@ -1,78 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is JavaScript Engine testing utilities.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Bertrand Le Roy
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//-----------------------------------------------------------------------------
var BUGNUMBER = 350256;
var summary = 'Array.apply maximum arguments: 2^19-1024';
var actual = '';
var expect = '';
expectExitCode(0);
expectExitCode(5);
//-----------------------------------------------------------------------------
test(Math.pow(2, 19)-1024);
//-----------------------------------------------------------------------------
function test(length)
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
var a = new Array();
a[length - 2] = 'length-2';
a[length - 1] = 'length-1';
var b = Array.apply(null, a);
expect = length + ',length-2,length-1';
actual = b.length + "," + b[length - 2] + "," + b[length - 1];
reportCompare(expect, actual, summary);
function f() {
return arguments.length + "," + arguments[length - 2] + "," +
arguments[length - 1];
}
expect = length + ',length-2,length-1';
actual = f.apply(null, a);
reportCompare(expect, actual, summary);
exitFunc ('test');
}

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

@ -58,7 +58,7 @@ inline uint32
ArgumentsObject::initialLength() const
{
uint32 argc = uint32(getSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
return argc;
}

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

@ -144,8 +144,7 @@ StackFrame::resetCallFrame(JSScript *script)
}
inline void
StackFrame::initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags,
void *ncode)
StackFrame::initJitFrameCallerHalf(StackFrame *prev, StackFrame::Flags flags, void *ncode)
{
JS_ASSERT((flags & ~(CONSTRUCTING |
FUNCTION |
@ -153,7 +152,7 @@ StackFrame::initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags,
UNDERFLOW_ARGS)) == 0);
flags_ = FUNCTION | flags;
prev_ = cx->fp();
prev_ = prev;
ncode_ = ncode;
}
@ -176,15 +175,11 @@ StackFrame::initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual)
inline bool
StackFrame::initJitFrameLatePrologue(JSContext *cx, Value **limit)
{
uintN nvals = script()->nslots + VALUES_PER_STACK_FRAME;
Value *required = (Value *)this + nvals;
if (required >= *limit) {
ContextStack &stack = cx->stack;
if (!stack.space().tryBumpLimit(NULL, slots(), nvals, limit)) {
stack.popFrameAfterOverflow();
js_ReportOverRecursed(cx);
return false;
}
*limit = cx->stack.space().getStackLimit(cx, DONT_REPORT_ERROR);
if (!*limit) {
cx->stack.popFrameAfterOverflow();
js_ReportOverRecursed(cx);
return false;
}
SetValueRangeToUndefined(slots(), script()->nfixed);
@ -383,97 +378,60 @@ StackFrame::markActivationObjectsAsPut()
#ifdef JS_TRACER
JS_ALWAYS_INLINE bool
StackSpace::ensureEnoughSpaceToEnterTrace()
StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
{
ptrdiff_t needed = TraceNativeStorage::MAX_NATIVE_STACK_SLOTS +
TraceNativeStorage::MAX_CALL_STACK_ENTRIES * VALUES_PER_STACK_FRAME;
#ifdef XP_WIN
return ensureSpace(NULL, firstUnused(), needed);
#else
return end_ - firstUnused() > needed;
#endif
return ensureSpace(cx, DONT_REPORT_ERROR, firstUnused(), needed);
}
#endif
STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
JS_ALWAYS_INLINE bool
StackSpace::ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
{
assertInvariants();
JS_ASSERT(from >= firstUnused());
#ifdef XP_WIN
JS_ASSERT(from <= commitEnd_);
if (commitEnd_ - from < nvals)
return bumpCommit(maybecx, from, nvals);
return true;
#else
if (end_ - from < nvals) {
js_ReportOverRecursed(maybecx);
return false;
}
return true;
#endif
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
return ensureSpaceSlow(cx, report, from, nvals);
return true;
}
inline Value *
StackSpace::getStackLimit(JSContext *cx)
StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
{
Value *limit;
#ifdef XP_WIN
limit = commitEnd_;
#else
limit = end_;
#endif
/* See getStackLimit comment in Stack.h. */
FrameRegs &regs = cx->regs();
uintN minSpace = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
if (regs.sp + minSpace > limit) {
js_ReportOverRecursed(cx);
return NULL;
}
return limit;
uintN nvals = regs.fp()->numSlots() + VALUES_PER_STACK_FRAME;
return ensureSpace(cx, report, regs.sp, nvals)
? conservativeEnd_
: NULL;
}
/*****************************************************************************/
JS_ALWAYS_INLINE bool
OOMCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
JS_ALWAYS_INLINE StackFrame *
ContextStack::getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
JSFunction *fun, JSScript *script, StackFrame::Flags *flags) const
{
return space.ensureSpace(cx, from, nvals);
}
JS_ASSERT(fun->script() == script);
uintN nformal = fun->nargs;
Value *firstUnused = args.end();
JS_ASSERT(firstUnused == space().firstUnused());
JS_ALWAYS_INLINE bool
LimitCheck::operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
{
/*
* Include an extra sizeof(StackFrame) to satisfy the method-jit
* stackLimit invariant.
*/
nvals += VALUES_PER_STACK_FRAME;
JS_ASSERT(from < *limit);
if (*limit - from >= ptrdiff_t(nvals))
return true;
return space.tryBumpLimit(cx, from, nvals, limit);
}
template <class Check>
JS_ALWAYS_INLINE StackFrame *
ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
JSFunction *fun, JSScript *script,
StackFrame::Flags *flags, Check check) const
{
JS_ASSERT(fun->script() == script);
JS_ASSERT(space().firstUnused() == args.end());
Value *firstUnused = args.end();
uintN nvals = VALUES_PER_STACK_FRAME + script->nslots;
uintN nformal = fun->nargs;
uintN nvals = 2 * VALUES_PER_STACK_FRAME + script->nslots;
/* Maintain layout invariant: &formalArgs[0] == ((Value *)fp) - nformal. */
if (args.argc() == nformal) {
if (JS_UNLIKELY(!check(cx, space(), firstUnused, nvals)))
if (!space().ensureSpace(cx, report, firstUnused, nvals))
return NULL;
return reinterpret_cast<StackFrame *>(firstUnused);
}
@ -481,7 +439,7 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
if (args.argc() < nformal) {
*flags = StackFrame::Flags(*flags | StackFrame::UNDERFLOW_ARGS);
uintN nmissing = nformal - args.argc();
if (JS_UNLIKELY(!check(cx, space(), firstUnused, nmissing + nvals)))
if (!space().ensureSpace(cx, report, firstUnused, nmissing + nvals))
return NULL;
SetValueRangeToUndefined(firstUnused, nmissing);
return reinterpret_cast<StackFrame *>(firstUnused + nmissing);
@ -489,20 +447,18 @@ ContextStack::getCallFrame(JSContext *cx, const CallArgs &args,
*flags = StackFrame::Flags(*flags | StackFrame::OVERFLOW_ARGS);
uintN ncopy = 2 + nformal;
if (JS_UNLIKELY(!check(cx, space(), firstUnused, ncopy + nvals)))
if (!space().ensureSpace(cx, report, firstUnused, ncopy + nvals))
return NULL;
Value *dst = firstUnused;
Value *src = args.base();
PodCopy(dst, src, ncopy);
return reinterpret_cast<StackFrame *>(firstUnused + ncopy);
}
template <class Check>
JS_ALWAYS_INLINE bool
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Check check)
MaybeConstruct construct)
{
JS_ASSERT(onTop());
JS_ASSERT(&regs == &seg_->regs());
@ -512,7 +468,7 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &ar
JS_ASSERT(fun->script() == script);
StackFrame::Flags flags = ToFrameFlags(construct);
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
if (!fp)
return false;
@ -522,25 +478,36 @@ ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &ar
return true;
}
JS_ALWAYS_INLINE bool
ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Value **stackLimit)
{
if (!pushInlineFrame(cx, regs, args, callee, fun, script, construct))
return false;
*stackLimit = space().conservativeEnd_;
return true;
}
JS_ALWAYS_INLINE StackFrame *
ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check)
ContextStack::getFixupFrame(JSContext *cx, MaybeReportError report,
const CallArgs &args, JSFunction *fun, JSScript *script,
void *ncode, MaybeConstruct construct, Value **stackLimit)
{
JS_ASSERT(onTop());
JS_ASSERT(&regs == &cx->regs());
JS_ASSERT(regs.sp == args.end());
JS_ASSERT(args.callee().getFunctionPrivate() == fun);
JS_ASSERT(fun->script() == script);
StackFrame::Flags flags = ToFrameFlags(construct);
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
StackFrame *fp = getCallFrame(cx, report, args, fun, script, &flags);
if (!fp)
return NULL;
/* Do not init late prologue or regs; this is done by jit code. */
fp->initJitFrameCallerHalf(cx, flags, ncode);
fp->initJitFrameCallerHalf(cx->fp(), flags, ncode);
fp->initJitFrameEarlyPrologue(fun, args.argc());
*stackLimit = space().conservativeEnd_;
return fp;
}
@ -648,7 +615,7 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
return false;
/* Otherwise, element values are on the stack. */
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(fp->numActualArgs() <= StackSpace::ARGS_LENGTH_MAX);
return fp->forEachCanonicalActualArg(detail::CopyNonHoleArgsTo(this, vp), start, count);
}

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

@ -302,11 +302,17 @@ StackSegment::popCall()
/*****************************************************************************/
StackSpace::StackSpace()
: base_(NULL),
: seg_(NULL),
base_(NULL),
conservativeEnd_(NULL),
#ifdef XP_WIN
commitEnd_(NULL),
end_(NULL),
seg_(NULL)
{}
#endif
defaultEnd_(NULL),
trustedEnd_(NULL)
{
assertInvariants();
}
bool
StackSpace::init()
@ -320,27 +326,32 @@ StackSpace::init()
if (p != check)
return false;
base_ = reinterpret_cast<Value *>(p);
commitEnd_ = base_ + COMMIT_VALS;
end_ = base_ + CAPACITY_VALS;
conservativeEnd_ = commitEnd_ = base_ + COMMIT_VALS;
trustedEnd_ = base_ + CAPACITY_VALS;
defaultEnd_ = trustedEnd_ - BUFFER_VALS;
#elif defined(XP_OS2)
if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
return false;
base_ = reinterpret_cast<Value *>(p);
end_ = commitEnd_ = base_ + CAPACITY_VALS;
trustedEnd_ = base_ + CAPACITY_VALS;
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
#else
JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
return false;
base_ = reinterpret_cast<Value *>(p);
end_ = commitEnd_ = base_ + CAPACITY_VALS;
trustedEnd_ = base_ + CAPACITY_VALS;
conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
#endif
assertInvariants();
return true;
}
StackSpace::~StackSpace()
{
assertInvariants();
JS_ASSERT(!seg_);
if (!base_)
return;
@ -402,55 +413,75 @@ StackSpace::mark(JSTracer *trc)
}
}
#ifdef XP_WIN
JS_FRIEND_API(bool)
StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const
{
if (end_ - from < nvals) {
js_ReportOverRecursed(maybecx);
assertInvariants();
bool trusted = !cx->compartment ||
cx->compartment->principals == cx->runtime->trustedPrincipals();
Value *end = trusted ? trustedEnd_ : defaultEnd_;
/*
* conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
* to be bumped past defaultEnd_, untrusted JS would be able to consume the
* buffer space at the end of the stack reserved for trusted JS.
*/
if (end - from < nvals) {
if (report)
js_ReportOverRecursed(cx);
return false;
}
Value *newCommit = commitEnd_;
Value *request = from + nvals;
#ifdef XP_WIN
if (commitEnd_ - from < nvals) {
Value *newCommit = commitEnd_;
Value *request = from + nvals;
/* Use a dumb loop; will probably execute once. */
JS_ASSERT((end_ - newCommit) % COMMIT_VALS == 0);
do {
newCommit += COMMIT_VALS;
JS_ASSERT((end_ - newCommit) >= 0);
} while (newCommit < request);
/* Use a dumb loop; will probably execute once. */
JS_ASSERT((trustedEnd_ - newCommit) % COMMIT_VALS == 0);
do {
newCommit += COMMIT_VALS;
JS_ASSERT((trustedEnd_ - newCommit) >= 0);
} while (newCommit < request);
/* The cast is safe because CAPACITY_BYTES is small. */
int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
/* The cast is safe because CAPACITY_BYTES is small. */
int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
js_ReportOverRecursed(maybecx);
return false;
if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
if (report)
js_ReportOverRecursed(cx);
return false;
}
commitEnd_ = newCommit;
conservativeEnd_ = Min(commitEnd_, defaultEnd_);
assertInvariants();
}
#endif
commitEnd_ = newCommit;
return true;
}
#endif
bool
StackSpace::tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit)
StackSpace::tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit)
{
if (!ensureSpace(maybecx, from, nvals))
if (!ensureSpace(cx, REPORT_ERROR, from, nvals))
return false;
#ifdef XP_WIN
*limit = commitEnd_;
#else
*limit = end_;
#endif
*limit = conservativeEnd_;
return true;
}
size_t
StackSpace::committedSize()
{
#ifdef XP_WIN
return (commitEnd_ - base_) * sizeof(Value);
#else
return (trustedEnd_ - base_) * sizeof(Value);
#endif
}
/*****************************************************************************/
@ -516,17 +547,18 @@ ContextStack::containsSlow(const StackFrame *target) const
* there is space for nvars slots on top of the stack.
*/
Value *
ContextStack::ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg)
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg)
{
Value *firstUnused = space().firstUnused();
if (onTop() && extend) {
if (!space().ensureSpace(cx, firstUnused, nvars))
if (!space().ensureSpace(cx, report, firstUnused, nvars))
return NULL;
return firstUnused;
}
if (!space().ensureSpace(cx, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
return NULL;
FrameRegs *regs;
@ -559,7 +591,7 @@ bool
ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *iag)
{
uintN nvars = 2 + argc;
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &iag->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &iag->pushedSeg_);
if (!firstUnused)
return false;
@ -595,7 +627,7 @@ ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
JSScript *script = fun->script();
StackFrame::Flags flags = ToFrameFlags(construct);
StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, OOMCheck());
StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
if (!fp)
return false;
@ -642,7 +674,7 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
}
uintN nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
Value *firstUnused = ensureOnTop(cx, nvars, extend, &efg->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
if (!firstUnused)
return NULL;
@ -662,10 +694,11 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
}
bool
ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg)
{
uintN nvars = VALUES_PER_STACK_FRAME;
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &dfg->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
if (!firstUnused)
return NULL;
@ -709,7 +742,7 @@ ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrame
uintN vplen = (Value *)genfp - genvp;
uintN nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots();
Value *firstUnused = ensureOnTop(cx, nvars, CAN_EXTEND, &gfg->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
if (!firstUnused)
return false;
@ -755,14 +788,30 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
bool
ContextStack::saveFrameChain()
{
/*
* The StackSpace uses the context's current compartment to determine
* whether to allow access to the privileged end-of-stack buffer.
* However, we always want saveFrameChain to have access to this privileged
* buffer since it gets used to prepare calling trusted JS. To force this,
* we clear the current compartment (which is interpreted by ensureSpace as
* 'trusted') and either restore it on OOM or let resetCompartment()
* clobber it.
*/
JSCompartment *original = cx_->compartment;
cx_->compartment = NULL;
bool pushedSeg;
if (!ensureOnTop(cx_, 0, CANT_EXTEND, &pushedSeg))
if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
cx_->compartment = original;
js_ReportOverRecursed(cx_);
return false;
}
JS_ASSERT(pushedSeg);
JS_ASSERT(!hasfp());
cx_->resetCompartment();
JS_ASSERT(onTop() && seg_->isEmpty());
cx_->resetCompartment();
return true;
}

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

@ -387,7 +387,7 @@ class StackFrame
void resetCallFrame(JSScript *script);
/* Called by jit stubs and serve as a specification for jit-code. */
void initJitFrameCallerHalf(JSContext *cx, StackFrame::Flags flags, void *ncode);
void initJitFrameCallerHalf(StackFrame *prev, StackFrame::Flags flags, void *ncode);
void initJitFrameEarlyPrologue(JSFunction *fun, uint32 nactual);
bool initJitFrameLatePrologue(JSContext *cx, Value **limit);
@ -1286,29 +1286,49 @@ JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
class StackSpace
{
Value *base_;
mutable Value *commitEnd_;
Value *end_;
StackSegment *seg_;
Value *base_;
mutable Value *conservativeEnd_;
#ifdef XP_WIN
mutable Value *commitEnd_;
#endif
Value *defaultEnd_;
Value *trustedEnd_;
void assertInvariants() const {
JS_ASSERT(base_ <= conservativeEnd_);
#ifdef XP_WIN
JS_ASSERT(conservativeEnd_ <= commitEnd_);
JS_ASSERT(commitEnd_ <= trustedEnd_);
#endif
JS_ASSERT(conservativeEnd_ <= defaultEnd_);
JS_ASSERT(defaultEnd_ <= trustedEnd_);
}
/* The total number of values/bytes reserved for the stack. */
static const size_t CAPACITY_VALS = 512 * 1024;
static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
/* How much of the stack is initially committed. */
static const size_t COMMIT_VALS = 16 * 1024;
static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value);
/* How much space is reserved at the top of the stack for trusted JS. */
static const size_t BUFFER_VALS = 16 * 1024;
static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value);
static void staticAsserts() {
JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
}
#ifdef XP_WIN
JS_FRIEND_API(bool) bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
#endif
friend class AllFramesIter;
friend class ContextStack;
friend class StackFrame;
friend class OOMCheck;
inline bool ensureSpace(JSContext *maybecx, Value *from, ptrdiff_t nvals) const;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
StackSegment &findContainingSegment(const StackFrame *target) const;
public:
@ -1316,9 +1336,21 @@ class StackSpace
bool init();
~StackSpace();
/*
* Maximum supported value of arguments.length. This bounds the maximum
* number of arguments that can be supplied to Function.prototype.apply.
* This value also bounds the number of elements parsed in an array
* initialiser.
*
* Since arguments are copied onto the stack, the stack size is the
* limiting factor for this constant. Use the max stack size (available to
* untrusted code) with an extra buffer so that, after such an apply, the
* callee can do a little work without OOMing.
*/
static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
/* See stack layout comment above. */
Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
Value *endOfSpace() const { return end_; }
#ifdef JS_TRACER
/*
@ -1326,8 +1358,11 @@ class StackSpace
* good way to handle an OOM for these allocations, so this function checks
* that OOM cannot occur using the size of the TraceNativeStorage as a
* conservative upper bound.
*
* Despite taking a 'cx', this function does not report an error if it
* returns 'false'.
*/
inline bool ensureEnoughSpaceToEnterTrace();
inline bool ensureEnoughSpaceToEnterTrace(JSContext *cx);
#endif
/*
@ -1344,8 +1379,8 @@ class StackSpace
* does indeed have this required space and reports an error and returns
* NULL if this reserve space cannot be allocated.
*/
inline Value *getStackLimit(JSContext *cx);
bool tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit);
inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
/* Called during GC: mark segments, frames, and slots under firstUnused. */
void mark(JSTracer *trc);
@ -1356,36 +1391,6 @@ class StackSpace
/*****************************************************************************/
/*
* For pushInlineFrame, there are three different ways the caller may want to
* check for stack overflow:
* - NoCheck: the caller has already ensured there is enough space
* - OOMCheck: perform normal checking against the end of the stack
* - LimitCheck: check against the given stack limit (see getStackLimit)
*/
class NoCheck
{
public:
bool operator()(JSContext *, StackSpace &, Value *, uintN) { return true; }
};
class OOMCheck
{
public:
bool operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals);
};
class LimitCheck
{
Value **limit;
public:
LimitCheck(Value **limit) : limit(limit) {}
bool operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals);
};
/*****************************************************************************/
class ContextStack
{
StackSegment *seg_;
@ -1411,13 +1416,12 @@ class ContextStack
/* Implementation details of push* public interface. */
StackSegment *pushSegment(JSContext *cx);
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
Value *ensureOnTop(JSContext *cx, uintN nvars, MaybeExtend extend, bool *pushedSeg);
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg);
/* Check = { OOMCheck, LimitCheck } */
template <class Check>
inline StackFrame *
getCallFrame(JSContext *cx, const CallArgs &args, JSFunction *fun, JSScript *script,
StackFrame::Flags *pflags, Check check) const;
getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
JSFunction *fun, JSScript *script, StackFrame::Flags *pflags) const;
/* Make pop* functions private since only called by guard classes. */
void popSegment();
@ -1495,17 +1499,20 @@ class ContextStack
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
/* Pushes a "dummy" frame; should be removed one day. */
bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg);
/*
* An "inline frame" may only be pushed from within the top, active
* segment. This is the case for calls made inside mjit code and Interpret.
* For the Check parameter, see OOMCheck et al above.
* The 'stackLimit' overload updates 'stackLimit' if it changes.
*/
template <class Check>
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Check check);
MaybeConstruct construct);
bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSObject &callee, JSFunction *fun, JSScript *script,
MaybeConstruct construct, Value **stackLimit);
void popInlineFrame(FrameRegs &regs);
/* Pop a partially-pushed frame after hitting the limit before throwing. */
@ -1519,9 +1526,9 @@ class ContextStack
* getFixupFrame = pushInlineFrame -
* (fp->initJitFrameLatePrologue + regs->prepareToRun)
*/
StackFrame *getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
JSFunction *fun, JSScript *script, void *ncode,
MaybeConstruct construct, LimitCheck check);
StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
const CallArgs &args, JSFunction *fun, JSScript *script,
void *ncode, MaybeConstruct construct, Value **stackLimit);
bool saveFrameChain();
void restoreFrameChain();