1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jsapi-tests/tests.h"
8
9 static TestJSPrincipals system_principals(1);
10
11 static const JSClassOps global_classOps = {
12 nullptr,
13 nullptr,
14 nullptr,
15 nullptr,
16 nullptr,
17 nullptr,
18 nullptr,
19 nullptr,
20 nullptr,
21 nullptr,
22 nullptr,
23 JS_GlobalObjectTraceHook
24 };
25
26 static const JSClass global_class = {
27 "global",
28 JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS,
29 &global_classOps
30 };
31
32 static JS::PersistentRootedObject trusted_glob;
33 static JS::PersistentRootedObject trusted_fun;
34
35 static bool
CallTrusted(JSContext * cx,unsigned argc,JS::Value * vp)36 CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp)
37 {
38 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
39
40 bool ok = false;
41 {
42 JSAutoCompartment ac(cx, trusted_glob);
43 JS::RootedValue funVal(cx, JS::ObjectValue(*trusted_fun));
44 ok = JS_CallFunctionValue(cx, nullptr, funVal, JS::HandleValueArray::empty(), args.rval());
45 }
46 return ok;
47 }
48
BEGIN_TEST(testChromeBuffer)49 BEGIN_TEST(testChromeBuffer)
50 {
51 JS_SetTrustedPrincipals(cx, &system_principals);
52
53 JS::CompartmentOptions options;
54 trusted_glob.init(cx, JS_NewGlobalObject(cx, &global_class, &system_principals,
55 JS::FireOnNewGlobalHook, options));
56 CHECK(trusted_glob);
57
58 JS::RootedFunction fun(cx);
59
60 /*
61 * Check that, even after untrusted content has exhausted the stack, code
62 * compiled with "trusted principals" can run using reserved trusted-only
63 * buffer space.
64 */
65 {
66 // Disable the JIT because if we don't this test fails. See bug 1160414.
67 JS::ContextOptions oldOptions = JS::ContextOptionsRef(cx);
68 JS::ContextOptionsRef(cx).setIon(false).setBaseline(false);
69 {
70 JSAutoCompartment ac(cx, trusted_glob);
71 const char* paramName = "x";
72 const char* bytes = "return x ? 1 + trusted(x-1) : 0";
73 JS::CompileOptions options(cx);
74 options.setFileAndLine("", 0);
75 JS::AutoObjectVector emptyScopeChain(cx);
76 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "trusted",
77 1, ¶mName, bytes, strlen(bytes), &fun));
78 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, JSPROP_ENUMERATE));
79 trusted_fun.init(cx, JS_GetFunctionObject(fun));
80 }
81
82 JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
83 CHECK(JS_WrapValue(cx, &v));
84
85 const char* paramName = "trusted";
86 const char* bytes = "try { "
87 " return untrusted(trusted); "
88 "} catch (e) { "
89 " try { "
90 " return trusted(100); "
91 " } catch(e) { "
92 " return -1; "
93 " } "
94 "} ";
95 JS::CompileOptions options(cx);
96 options.setFileAndLine("", 0);
97 JS::AutoObjectVector emptyScopeChain(cx);
98 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
99 ¶mName, bytes, strlen(bytes), &fun));
100 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
101
102 JS::RootedValue rval(cx);
103 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(v), &rval));
104 CHECK(rval.toInt32() == 100);
105 JS::ContextOptionsRef(cx) = oldOptions;
106 }
107
108 /*
109 * Check that content called from chrome in the reserved-buffer space
110 * immediately ooms.
111 */
112 {
113 {
114 JSAutoCompartment ac(cx, trusted_glob);
115 const char* paramName = "untrusted";
116 const char* bytes = "try { "
117 " untrusted(); "
118 "} catch (e) { "
119 " /* "
120 " * Careful! We must not reenter JS "
121 " * that might try to push a frame. "
122 " */ "
123 " return 'From trusted: ' + "
124 " e.name + ': ' + e.message; "
125 "} ";
126 JS::CompileOptions options(cx);
127 options.setFileAndLine("", 0);
128 JS::AutoObjectVector emptyScopeChain(cx);
129 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "trusted",
130 1, ¶mName, bytes, strlen(bytes), &fun));
131 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, JSPROP_ENUMERATE));
132 trusted_fun = JS_GetFunctionObject(fun);
133 }
134
135 JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
136 CHECK(JS_WrapValue(cx, &v));
137
138 const char* paramName = "trusted";
139 const char* bytes = "try { "
140 " return untrusted(trusted); "
141 "} catch (e) { "
142 " return trusted(untrusted); "
143 "} ";
144 JS::CompileOptions options(cx);
145 options.setFileAndLine("", 0);
146 JS::AutoObjectVector emptyScopeChain(cx);
147 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
148 ¶mName, bytes, strlen(bytes), &fun));
149 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
150
151 JS::RootedValue rval(cx);
152 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(v), &rval));
153 bool match;
154 CHECK(JS_StringEqualsAscii(cx, rval.toString(), "From trusted: InternalError: too much recursion", &match));
155 CHECK(match);
156 }
157
158 {
159 {
160 JSAutoCompartment ac(cx, trusted_glob);
161 const char* bytes = "return 42";
162 JS::CompileOptions options(cx);
163 options.setFileAndLine("", 0);
164 JS::AutoObjectVector emptyScopeChain(cx);
165 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "trusted",
166 0, nullptr, bytes, strlen(bytes), &fun));
167 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, JSPROP_ENUMERATE));
168 trusted_fun = JS_GetFunctionObject(fun);
169 }
170
171 JS::RootedFunction fun(cx, JS_NewFunction(cx, CallTrusted, 0, 0, "callTrusted"));
172 JS::RootedObject callTrusted(cx, JS_GetFunctionObject(fun));
173
174 const char* paramName = "f";
175 const char* bytes = "try { "
176 " return untrusted(trusted); "
177 "} catch (e) { "
178 " return f(); "
179 "} ";
180 JS::CompileOptions options(cx);
181 options.setFileAndLine("", 0);
182 JS::AutoObjectVector emptyScopeChain(cx);
183 CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "untrusted", 1,
184 ¶mName, bytes, strlen(bytes), &fun));
185 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE));
186
187 JS::RootedValue arg(cx, JS::ObjectValue(*callTrusted));
188 JS::RootedValue rval(cx);
189 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(arg), &rval));
190 CHECK(rval.toInt32() == 42);
191 }
192
193 return true;
194 }
195 END_TEST(testChromeBuffer)
196