Bug 834732 - Implement stricter cx handling in xpc_EvalInSandbox. r=mrbkap

This commit is contained in:
Bobby Holley 2013-02-26 11:04:11 -08:00
parent 51ba6ef50d
commit 1ffc542460

View File

@ -3865,6 +3865,8 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
JSVersion jsVersion, bool returnStringOnly, jsval *rval)
{
JS_AbortIfWrongThread(JS_GetRuntime(cx));
JSAutoRequest ar(cx);
*rval = JS::UndefinedValue();
bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandbox);
sandbox = js::UnwrapObjectChecked(sandbox);
@ -3876,10 +3878,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
(nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox);
NS_ASSERTION(sop, "Invalid sandbox passed");
nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
if (!prin) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
nsAutoCString filenameBuf;
if (!filename) {
@ -3889,114 +3888,81 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
lineNo = 1;
}
JSObject *callingScope;
// We create a separate cx to do the sandbox evaluation. Scope it.
JS::Value v = JS::UndefinedValue();
JS::Value exn = JS::UndefinedValue();
bool ok = true;
{
JSAutoRequest req(cx);
callingScope = JS_GetGlobalForScopeChain(cx);
if (!callingScope) {
return NS_ERROR_FAILURE;
// Make a special cx for the sandbox and push it.
// NB: As soon as the RefPtr goes away, the cx goes away. So declare
// it first so that it disappears last.
nsRefPtr<ContextHolder> sandcxHolder = new ContextHolder(cx, sandbox, prin);
JSContext *sandcx = sandcxHolder->GetJSContext();
if (!sandcx) {
JS_ReportError(cx, "Can't prepare context for evalInSandbox");
return NS_ERROR_OUT_OF_MEMORY;
}
}
nsCxPusher pusher;
pusher.Push(sandcx);
nsRefPtr<ContextHolder> sandcx = new ContextHolder(cx, sandbox, prin);
if (!sandcx || !sandcx->GetJSContext()) {
JS_ReportError(cx, "Can't prepare context for evalInSandbox");
return NS_ERROR_OUT_OF_MEMORY;
}
JSAutoRequest req(sandcx);
JSAutoCompartment ac(sandcx, sandbox);
if (jsVersion != JSVERSION_DEFAULT)
JS_SetVersion(sandcx->GetJSContext(), jsVersion);
if (jsVersion != JSVERSION_DEFAULT)
JS_SetVersion(sandcx, jsVersion);
XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack();
MOZ_ASSERT(stack);
if (!stack->Push(sandcx->GetJSContext())) {
JS_ReportError(cx, "Unable to initialize XPConnect with the sandbox context");
return NS_ERROR_FAILURE;
}
nsresult rv = NS_OK;
{
JSAutoRequest req(sandcx->GetJSContext());
JSAutoCompartment ac(sandcx->GetJSContext(), sandbox);
jsval v;
JSString *str = nullptr;
JS::CompileOptions options(sandcx->GetJSContext());
JS::CompileOptions options(sandcx);
options.setPrincipals(nsJSPrincipals::get(prin))
.setFileAndLine(filename, lineNo);
js::RootedObject rootedSandbox(sandcx->GetJSContext(), sandbox);
bool ok = JS::Evaluate(sandcx->GetJSContext(), rootedSandbox, options,
PromiseFlatString(source).get(), source.Length(), &v);
js::RootedObject rootedSandbox(sandcx, sandbox);
ok = JS::Evaluate(sandcx, rootedSandbox, options,
PromiseFlatString(source).get(), source.Length(), &v);
if (ok && returnStringOnly && !(JSVAL_IS_VOID(v))) {
ok = !!(str = JS_ValueToString(sandcx->GetJSContext(), v));
JSString *str = JS_ValueToString(sandcx, v);
ok = !!str;
v = ok ? JS::StringValue(str) : JS::UndefinedValue();
}
if (!ok) {
// The sandbox threw an exception, convert it to a string (if
// asked) or convert it to a SJOW.
jsval exn;
if (JS_GetPendingException(sandcx->GetJSContext(), &exn)) {
JS_ClearPendingException(sandcx->GetJSContext());
if (returnStringOnly) {
// The caller asked for strings only, convert the
// exception into a string.
str = JS_ValueToString(sandcx->GetJSContext(), exn);
if (str) {
// We converted the exception to a string. Use that
// as the value exception.
exn = STRING_TO_JSVAL(str);
if (JS_WrapValue(cx, &exn)) {
JS_SetPendingException(cx, exn);
} else {
JS_ClearPendingException(cx);
rv = NS_ERROR_FAILURE;
}
} else {
JS_ClearPendingException(cx);
rv = NS_ERROR_FAILURE;
}
} else {
if (JS_WrapValue(cx, &exn)) {
JS_SetPendingException(cx, exn);
}
}
// Clear str so we don't confuse callers.
str = nullptr;
} else {
rv = NS_ERROR_OUT_OF_MEMORY;
}
} else {
// Convert the result into something safe for our caller.
JSAutoRequest req(cx);
JSAutoCompartment ac(cx, callingScope);
if (str) {
v = STRING_TO_JSVAL(str);
}
// Transitively apply Xray waivers if |sb| was waived.
if (waiveXray && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
rv = NS_ERROR_FAILURE;
if (!waiveXray && !JS_WrapValue(cx, &v))
rv = NS_ERROR_FAILURE;
if (NS_SUCCEEDED(rv)) {
*rval = v;
// If the sandbox threw an exception, grab it off the context.
if (JS_GetPendingException(sandcx, &exn)) {
MOZ_ASSERT(!ok);
JS_ClearPendingException(sandcx);
if (returnStringOnly) {
// The caller asked for strings only, convert the
// exception into a string.
JSString *str = JS_ValueToString(sandcx, exn);
exn = str ? JS::StringValue(str) : JS::UndefinedValue();
}
}
}
if (stack)
unused << stack->Pop();
//
// Alright, we're back on the caller's cx. If an error occured, try to
// wrap and set the exception. Otherwise, wrap the return value.
//
return rv;
if (!ok) {
// If we end up without an exception, it was probably due to OOM along
// the way, in which case we thow. Otherwise, wrap it.
if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
return NS_ERROR_OUT_OF_MEMORY;
// Set the exception on our caller's cx.
JS_SetPendingException(cx, exn);
return NS_OK;
}
// Transitively apply Xray waivers if |sb| was waived.
if (waiveXray) {
ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
} else {
ok = JS_WrapValue(cx, &v);
}
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
// Whew!
*rval = v;
return NS_OK;
}
/* JSObject import (in AUTF8String registryLocation,