Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "jsapi-tests/tests.h" |
michael@0 | 8 | |
michael@0 | 9 | static TestJSPrincipals system_principals(1); |
michael@0 | 10 | |
michael@0 | 11 | static const JSClass global_class = { |
michael@0 | 12 | "global", |
michael@0 | 13 | JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS, |
michael@0 | 14 | JS_PropertyStub, |
michael@0 | 15 | JS_DeletePropertyStub, |
michael@0 | 16 | JS_PropertyStub, |
michael@0 | 17 | JS_StrictPropertyStub, |
michael@0 | 18 | JS_EnumerateStub, |
michael@0 | 19 | JS_ResolveStub, |
michael@0 | 20 | JS_ConvertStub, |
michael@0 | 21 | nullptr, |
michael@0 | 22 | nullptr, |
michael@0 | 23 | nullptr, |
michael@0 | 24 | nullptr, |
michael@0 | 25 | JS_GlobalObjectTraceHook |
michael@0 | 26 | }; |
michael@0 | 27 | |
michael@0 | 28 | static JS::Heap<JSObject *> trusted_glob; |
michael@0 | 29 | static JS::Heap<JSObject *> trusted_fun; |
michael@0 | 30 | |
michael@0 | 31 | static bool |
michael@0 | 32 | CallTrusted(JSContext *cx, unsigned argc, jsval *vp) |
michael@0 | 33 | { |
michael@0 | 34 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
michael@0 | 35 | |
michael@0 | 36 | if (!JS_SaveFrameChain(cx)) |
michael@0 | 37 | return false; |
michael@0 | 38 | |
michael@0 | 39 | bool ok = false; |
michael@0 | 40 | { |
michael@0 | 41 | JSAutoCompartment ac(cx, trusted_glob); |
michael@0 | 42 | JS::RootedValue funVal(cx, JS::ObjectValue(*trusted_fun)); |
michael@0 | 43 | ok = JS_CallFunctionValue(cx, JS::NullPtr(), funVal, JS::HandleValueArray::empty(), args.rval()); |
michael@0 | 44 | } |
michael@0 | 45 | JS_RestoreFrameChain(cx); |
michael@0 | 46 | return ok; |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | BEGIN_TEST(testChromeBuffer) |
michael@0 | 50 | { |
michael@0 | 51 | JS_SetTrustedPrincipals(rt, &system_principals); |
michael@0 | 52 | |
michael@0 | 53 | trusted_glob = JS_NewGlobalObject(cx, &global_class, &system_principals, JS::FireOnNewGlobalHook); |
michael@0 | 54 | CHECK(trusted_glob); |
michael@0 | 55 | |
michael@0 | 56 | if (!JS::AddNamedObjectRoot(cx, &trusted_glob, "trusted-global")) |
michael@0 | 57 | return false; |
michael@0 | 58 | |
michael@0 | 59 | JS::RootedFunction fun(cx); |
michael@0 | 60 | |
michael@0 | 61 | /* |
michael@0 | 62 | * Check that, even after untrusted content has exhausted the stack, code |
michael@0 | 63 | * compiled with "trusted principals" can run using reserved trusted-only |
michael@0 | 64 | * buffer space. |
michael@0 | 65 | */ |
michael@0 | 66 | { |
michael@0 | 67 | { |
michael@0 | 68 | JSAutoCompartment ac(cx, trusted_glob); |
michael@0 | 69 | const char *paramName = "x"; |
michael@0 | 70 | const char *bytes = "return x ? 1 + trusted(x-1) : 0"; |
michael@0 | 71 | JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); |
michael@0 | 72 | JS::CompileOptions options(cx); |
michael@0 | 73 | options.setFileAndLine("", 0); |
michael@0 | 74 | CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, ¶mName, |
michael@0 | 75 | bytes, strlen(bytes), options)); |
michael@0 | 76 | trusted_fun = JS_GetFunctionObject(fun); |
michael@0 | 77 | if (!JS::AddNamedObjectRoot(cx, &trusted_fun, "trusted-function")) |
michael@0 | 78 | return false; |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); |
michael@0 | 82 | CHECK(JS_WrapValue(cx, &v)); |
michael@0 | 83 | |
michael@0 | 84 | const char *paramName = "trusted"; |
michael@0 | 85 | const char *bytes = "try { " |
michael@0 | 86 | " return untrusted(trusted); " |
michael@0 | 87 | "} catch (e) { " |
michael@0 | 88 | " try { " |
michael@0 | 89 | " return trusted(100); " |
michael@0 | 90 | " } catch(e) { " |
michael@0 | 91 | " return -1; " |
michael@0 | 92 | " } " |
michael@0 | 93 | "} "; |
michael@0 | 94 | JS::CompileOptions options(cx); |
michael@0 | 95 | options.setFileAndLine("", 0); |
michael@0 | 96 | CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, |
michael@0 | 97 | bytes, strlen(bytes), options)); |
michael@0 | 98 | |
michael@0 | 99 | JS::RootedValue rval(cx); |
michael@0 | 100 | CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, v, &rval)); |
michael@0 | 101 | CHECK(JSVAL_TO_INT(rval) == 100); |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | /* |
michael@0 | 105 | * Check that content called from chrome in the reserved-buffer space |
michael@0 | 106 | * immediately ooms. |
michael@0 | 107 | */ |
michael@0 | 108 | { |
michael@0 | 109 | { |
michael@0 | 110 | JSAutoCompartment ac(cx, trusted_glob); |
michael@0 | 111 | const char *paramName = "untrusted"; |
michael@0 | 112 | const char *bytes = "try { " |
michael@0 | 113 | " untrusted(); " |
michael@0 | 114 | "} catch (e) { " |
michael@0 | 115 | " return 'From trusted: ' + e; " |
michael@0 | 116 | "} "; |
michael@0 | 117 | JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); |
michael@0 | 118 | JS::CompileOptions options(cx); |
michael@0 | 119 | options.setFileAndLine("", 0); |
michael@0 | 120 | CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, ¶mName, |
michael@0 | 121 | bytes, strlen(bytes), options)); |
michael@0 | 122 | trusted_fun = JS_GetFunctionObject(fun); |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); |
michael@0 | 126 | CHECK(JS_WrapValue(cx, &v)); |
michael@0 | 127 | |
michael@0 | 128 | const char *paramName = "trusted"; |
michael@0 | 129 | const char *bytes = "try { " |
michael@0 | 130 | " return untrusted(trusted); " |
michael@0 | 131 | "} catch (e) { " |
michael@0 | 132 | " return trusted(untrusted); " |
michael@0 | 133 | "} "; |
michael@0 | 134 | JS::CompileOptions options(cx); |
michael@0 | 135 | options.setFileAndLine("", 0); |
michael@0 | 136 | CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, |
michael@0 | 137 | bytes, strlen(bytes), options)); |
michael@0 | 138 | |
michael@0 | 139 | JS::RootedValue rval(cx); |
michael@0 | 140 | CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, v, &rval)); |
michael@0 | 141 | bool match; |
michael@0 | 142 | CHECK(JS_StringEqualsAscii(cx, JSVAL_TO_STRING(rval), "From trusted: InternalError: too much recursion", &match)); |
michael@0 | 143 | CHECK(match); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | /* |
michael@0 | 147 | * Check that JS_SaveFrameChain called on the way from content to chrome |
michael@0 | 148 | * (say, as done by XPCJSContextSTack::Push) works. |
michael@0 | 149 | */ |
michael@0 | 150 | { |
michael@0 | 151 | { |
michael@0 | 152 | JSAutoCompartment ac(cx, trusted_glob); |
michael@0 | 153 | const char *bytes = "return 42"; |
michael@0 | 154 | JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet()); |
michael@0 | 155 | JS::CompileOptions options(cx); |
michael@0 | 156 | options.setFileAndLine("", 0); |
michael@0 | 157 | CHECK(fun = JS_CompileFunction(cx, global, "trusted", 0, nullptr, |
michael@0 | 158 | bytes, strlen(bytes), options)); |
michael@0 | 159 | trusted_fun = JS_GetFunctionObject(fun); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | JS::RootedFunction fun(cx, JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted")); |
michael@0 | 163 | JS::RootedObject callTrusted(cx, JS_GetFunctionObject(fun)); |
michael@0 | 164 | |
michael@0 | 165 | const char *paramName = "f"; |
michael@0 | 166 | const char *bytes = "try { " |
michael@0 | 167 | " return untrusted(trusted); " |
michael@0 | 168 | "} catch (e) { " |
michael@0 | 169 | " return f(); " |
michael@0 | 170 | "} "; |
michael@0 | 171 | JS::CompileOptions options(cx); |
michael@0 | 172 | options.setFileAndLine("", 0); |
michael@0 | 173 | CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, ¶mName, |
michael@0 | 174 | bytes, strlen(bytes), options)); |
michael@0 | 175 | |
michael@0 | 176 | JS::RootedValue arg(cx, JS::ObjectValue(*callTrusted)); |
michael@0 | 177 | JS::RootedValue rval(cx); |
michael@0 | 178 | CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, arg, &rval)); |
michael@0 | 179 | CHECK(JSVAL_TO_INT(rval) == 42); |
michael@0 | 180 | } |
michael@0 | 181 | |
michael@0 | 182 | return true; |
michael@0 | 183 | } |
michael@0 | 184 | virtual void uninit() { |
michael@0 | 185 | trusted_glob = nullptr; |
michael@0 | 186 | trusted_fun = nullptr; |
michael@0 | 187 | JS::RemoveObjectRoot(cx, &trusted_glob); |
michael@0 | 188 | JS::RemoveObjectRoot(cx, &trusted_fun); |
michael@0 | 189 | JSAPITest::uninit(); |
michael@0 | 190 | } |
michael@0 | 191 | END_TEST(testChromeBuffer) |