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