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, &paramName, 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                                   &paramName, 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, &paramName, 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                                  &paramName, 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                                   &paramName, 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