michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jsapi-tests/tests.h" michael@0: michael@0: static TestJSPrincipals system_principals(1); michael@0: michael@0: static const JSClass global_class = { michael@0: "global", michael@0: JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS, michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: JS_GlobalObjectTraceHook michael@0: }; michael@0: michael@0: static JS::Heap trusted_glob; michael@0: static JS::Heap trusted_fun; michael@0: michael@0: static bool michael@0: CallTrusted(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: michael@0: if (!JS_SaveFrameChain(cx)) michael@0: return false; michael@0: michael@0: bool ok = false; michael@0: { michael@0: JSAutoCompartment ac(cx, trusted_glob); michael@0: JS::RootedValue funVal(cx, JS::ObjectValue(*trusted_fun)); michael@0: ok = JS_CallFunctionValue(cx, JS::NullPtr(), funVal, JS::HandleValueArray::empty(), args.rval()); michael@0: } michael@0: JS_RestoreFrameChain(cx); michael@0: return ok; michael@0: } michael@0: michael@0: BEGIN_TEST(testChromeBuffer) michael@0: { michael@0: JS_SetTrustedPrincipals(rt, &system_principals); michael@0: michael@0: trusted_glob = JS_NewGlobalObject(cx, &global_class, &system_principals, JS::FireOnNewGlobalHook); michael@0: CHECK(trusted_glob); michael@0: michael@0: if (!JS::AddNamedObjectRoot(cx, &trusted_glob, "trusted-global")) michael@0: return false; michael@0: michael@0: JS::RootedFunction fun(cx); michael@0: michael@0: /* michael@0: * Check that, even after untrusted content has exhausted the stack, code michael@0: * compiled with "trusted principals" can run using reserved trusted-only michael@0: * buffer space. michael@0: */ michael@0: { michael@0: { michael@0: JSAutoCompartment ac(cx, trusted_glob); michael@0: const char *paramName = "x"; michael@0: const char *bytes = "return x ? 1 + trusted(x-1) : 0"; michael@0: JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, ¶mName, michael@0: bytes, strlen(bytes), options)); michael@0: trusted_fun = JS_GetFunctionObject(fun); michael@0: if (!JS::AddNamedObjectRoot(cx, &trusted_fun, "trusted-function")) michael@0: return false; michael@0: } michael@0: michael@0: JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); michael@0: CHECK(JS_WrapValue(cx, &v)); michael@0: michael@0: const char *paramName = "trusted"; michael@0: const char *bytes = "try { " michael@0: " return untrusted(trusted); " michael@0: "} catch (e) { " michael@0: " try { " michael@0: " return trusted(100); " michael@0: " } catch(e) { " michael@0: " return -1; " michael@0: " } " michael@0: "} "; michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, michael@0: bytes, strlen(bytes), options)); michael@0: michael@0: JS::RootedValue rval(cx); michael@0: CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, v, &rval)); michael@0: CHECK(JSVAL_TO_INT(rval) == 100); michael@0: } michael@0: michael@0: /* michael@0: * Check that content called from chrome in the reserved-buffer space michael@0: * immediately ooms. michael@0: */ michael@0: { michael@0: { michael@0: JSAutoCompartment ac(cx, trusted_glob); michael@0: const char *paramName = "untrusted"; michael@0: const char *bytes = "try { " michael@0: " untrusted(); " michael@0: "} catch (e) { " michael@0: " return 'From trusted: ' + e; " michael@0: "} "; michael@0: JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, ¶mName, michael@0: bytes, strlen(bytes), options)); michael@0: trusted_fun = JS_GetFunctionObject(fun); michael@0: } michael@0: michael@0: JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); michael@0: CHECK(JS_WrapValue(cx, &v)); michael@0: michael@0: const char *paramName = "trusted"; michael@0: const char *bytes = "try { " michael@0: " return untrusted(trusted); " michael@0: "} catch (e) { " michael@0: " return trusted(untrusted); " michael@0: "} "; michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, michael@0: bytes, strlen(bytes), options)); michael@0: michael@0: JS::RootedValue rval(cx); michael@0: CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, v, &rval)); michael@0: bool match; michael@0: CHECK(JS_StringEqualsAscii(cx, JSVAL_TO_STRING(rval), "From trusted: InternalError: too much recursion", &match)); michael@0: CHECK(match); michael@0: } michael@0: michael@0: /* michael@0: * Check that JS_SaveFrameChain called on the way from content to chrome michael@0: * (say, as done by XPCJSContextSTack::Push) works. michael@0: */ michael@0: { michael@0: { michael@0: JSAutoCompartment ac(cx, trusted_glob); michael@0: const char *bytes = "return 42"; michael@0: JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "trusted", 0, nullptr, michael@0: bytes, strlen(bytes), options)); michael@0: trusted_fun = JS_GetFunctionObject(fun); michael@0: } michael@0: michael@0: JS::RootedFunction fun(cx, JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted")); michael@0: JS::RootedObject callTrusted(cx, JS_GetFunctionObject(fun)); michael@0: michael@0: const char *paramName = "f"; michael@0: const char *bytes = "try { " michael@0: " return untrusted(trusted); " michael@0: "} catch (e) { " michael@0: " return f(); " michael@0: "} "; michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine("", 0); michael@0: CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, michael@0: bytes, strlen(bytes), options)); michael@0: michael@0: JS::RootedValue arg(cx, JS::ObjectValue(*callTrusted)); michael@0: JS::RootedValue rval(cx); michael@0: CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, arg, &rval)); michael@0: CHECK(JSVAL_TO_INT(rval) == 42); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: virtual void uninit() { michael@0: trusted_glob = nullptr; michael@0: trusted_fun = nullptr; michael@0: JS::RemoveObjectRoot(cx, &trusted_glob); michael@0: JS::RemoveObjectRoot(cx, &trusted_fun); michael@0: JSAPITest::uninit(); michael@0: } michael@0: END_TEST(testChromeBuffer)