testChromeBuffer.cpp (10550B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 "mozilla/Utf8.h" // mozilla::Utf8Unit 8 9 #include "js/CallAndConstruct.h" // JS_CallFunctionValue 10 #include "js/CompilationAndEvaluation.h" // JS::CompileFunction 11 #include "js/ContextOptions.h" 12 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 13 #include "js/GlobalObject.h" // JS_NewGlobalObject 14 #include "js/PropertyAndElement.h" // JS_DefineProperty 15 #include "js/SourceText.h" // JS::Source{Ownership,Text} 16 #include "jsapi-tests/tests.h" 17 #include "util/Text.h" 18 19 MOZ_RUNINIT static TestJSPrincipals system_principals(1); 20 21 static const JSClass global_class = { 22 "global", 23 JSCLASS_IS_GLOBAL | JSCLASS_GLOBAL_FLAGS, 24 &JS::DefaultGlobalClassOps, 25 }; 26 27 MOZ_RUNINIT static JS::PersistentRootedObject trusted_glob; 28 MOZ_RUNINIT static JS::PersistentRootedObject trusted_fun; 29 30 static bool CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp) { 31 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 32 33 bool ok = false; 34 { 35 JSAutoRealm ar(cx, trusted_glob); 36 JS::RootedValue funVal(cx, JS::ObjectValue(*trusted_fun)); 37 ok = JS_CallFunctionValue(cx, nullptr, funVal, 38 JS::HandleValueArray::empty(), args.rval()); 39 } 40 return ok; 41 } 42 43 BEGIN_TEST(testChromeBuffer) { 44 JS_SetTrustedPrincipals(cx, &system_principals); 45 46 JS::RealmOptions options; 47 trusted_glob.init(cx, 48 JS_NewGlobalObject(cx, &global_class, &system_principals, 49 JS::FireOnNewGlobalHook, options)); 50 CHECK(trusted_glob); 51 52 JS::RootedFunction fun(cx); 53 54 /* 55 * Check that, even after untrusted content has exhausted the stack, code 56 * compiled with "trusted principals" can run using reserved trusted-only 57 * buffer space. 58 */ 59 { 60 // Disable the JIT because if we don't this test fails. See bug 1160414. 61 uint32_t oldBaselineInterpreterEnabled; 62 CHECK(JS_GetGlobalJitCompilerOption( 63 cx, JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE, 64 &oldBaselineInterpreterEnabled)); 65 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE, 66 0); 67 uint32_t oldBaselineJitEnabled; 68 CHECK(JS_GetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_ENABLE, 69 &oldBaselineJitEnabled)); 70 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_ENABLE, 0); 71 #ifdef ENABLE_PORTABLE_BASELINE_INTERP 72 uint32_t oldPortableBaselineInterpreterEnabled; 73 CHECK(JS_GetGlobalJitCompilerOption( 74 cx, JSJITCOMPILER_PORTABLE_BASELINE_ENABLE, 75 &oldPortableBaselineInterpreterEnabled)); 76 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_PORTABLE_BASELINE_ENABLE, 77 0); 78 #endif 79 { 80 JSAutoRealm ar(cx, trusted_glob); 81 const char* paramName = "x"; 82 static const char bytes[] = "return x ? 1 + trusted(x-1) : 0"; 83 84 JS::SourceText<mozilla::Utf8Unit> srcBuf; 85 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 86 JS::SourceOwnership::Borrowed)); 87 88 JS::CompileOptions options(cx); 89 options.setFileAndLine("", 0); 90 91 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 92 fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1, 93 ¶mName, srcBuf); 94 CHECK(fun); 95 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, 96 JSPROP_ENUMERATE)); 97 trusted_fun.init(cx, JS_GetFunctionObject(fun)); 98 } 99 100 JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); 101 CHECK(JS_WrapValue(cx, &v)); 102 103 const char* paramName = "trusted"; 104 static const char bytes[] = 105 "try { " 106 " return untrusted(trusted); " 107 "} catch (e) { " 108 " try { " 109 " return trusted(100); " 110 " } catch(e) { " 111 " return -1; " 112 " } " 113 "} "; 114 115 JS::SourceText<mozilla::Utf8Unit> srcBuf; 116 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 117 JS::SourceOwnership::Borrowed)); 118 119 JS::CompileOptions options(cx); 120 options.setFileAndLine("", 0); 121 122 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 123 fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1, 124 ¶mName, srcBuf); 125 CHECK(fun); 126 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); 127 128 JS::RootedValue rval(cx); 129 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(v), &rval)); 130 CHECK(rval.toInt32() == 100); 131 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE, 132 oldBaselineInterpreterEnabled); 133 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_ENABLE, 134 oldBaselineJitEnabled); 135 #ifdef ENABLE_PORTABLE_BASELINE_INTERP 136 JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_PORTABLE_BASELINE_ENABLE, 137 oldPortableBaselineInterpreterEnabled); 138 #endif 139 } 140 141 /* 142 * Check that content called from chrome in the reserved-buffer space 143 * immediately ooms. 144 */ 145 { 146 { 147 JSAutoRealm ar(cx, trusted_glob); 148 149 const char* paramName = "untrusted"; 150 static const char bytes[] = 151 "try { " 152 " untrusted(); " 153 "} catch (e) { " 154 " /* " 155 " * Careful! We must not reenter JS " 156 " * that might try to push a frame. " 157 " */ " 158 " return 'From trusted: ' + " 159 " e.name + ': ' + e.message; " 160 "} "; 161 162 JS::SourceText<mozilla::Utf8Unit> srcBuf; 163 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 164 JS::SourceOwnership::Borrowed)); 165 166 JS::CompileOptions options(cx); 167 options.setFileAndLine("", 0); 168 169 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 170 fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 1, 171 ¶mName, srcBuf); 172 CHECK(fun); 173 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, 174 JSPROP_ENUMERATE)); 175 trusted_fun = JS_GetFunctionObject(fun); 176 } 177 178 JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun)); 179 CHECK(JS_WrapValue(cx, &v)); 180 181 const char* paramName = "trusted"; 182 static const char bytes[] = 183 "try { " 184 " return untrusted(trusted); " 185 "} catch (e) { " 186 " return trusted(untrusted); " 187 "} "; 188 189 JS::SourceText<mozilla::Utf8Unit> srcBuf; 190 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 191 JS::SourceOwnership::Borrowed)); 192 193 JS::CompileOptions options(cx); 194 options.setFileAndLine("", 0); 195 196 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 197 fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1, 198 ¶mName, srcBuf); 199 CHECK(fun); 200 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); 201 202 JS::RootedValue rval(cx); 203 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(v), &rval)); 204 #ifndef JS_SIMULATOR_ARM64 205 // The ARM64 simulator does not share a common implementation with the other 206 // simulators, and has slightly different end-of-stack behavior. Instead of 207 // failing with "too much recursion," it executes one more function call and 208 // fails with a type error. This behavior is not incorrect. 209 bool match; 210 CHECK(JS_StringEqualsAscii( 211 cx, rval.toString(), "From trusted: InternalError: too much recursion", 212 &match)); 213 CHECK(match); 214 #endif 215 } 216 217 { 218 { 219 JSAutoRealm ar(cx, trusted_glob); 220 221 static const char bytes[] = "return 42"; 222 223 JS::SourceText<mozilla::Utf8Unit> srcBuf; 224 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 225 JS::SourceOwnership::Borrowed)); 226 227 JS::CompileOptions options(cx); 228 options.setFileAndLine("", 0); 229 230 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 231 fun = JS::CompileFunction(cx, emptyEnvChain, options, "trusted", 0, 232 nullptr, srcBuf); 233 CHECK(fun); 234 CHECK(JS_DefineProperty(cx, trusted_glob, "trusted", fun, 235 JSPROP_ENUMERATE)); 236 trusted_fun = JS_GetFunctionObject(fun); 237 } 238 239 JS::RootedFunction fun( 240 cx, JS_NewFunction(cx, CallTrusted, 0, 0, "callTrusted")); 241 JS::RootedObject callTrusted(cx, JS_GetFunctionObject(fun)); 242 243 const char* paramName = "f"; 244 static const char bytes[] = 245 "try { " 246 " return untrusted(trusted); " 247 "} catch (e) { " 248 " return f(); " 249 "} "; 250 251 JS::SourceText<mozilla::Utf8Unit> srcBuf; 252 CHECK(srcBuf.init(cx, bytes, js_strlen(bytes), 253 JS::SourceOwnership::Borrowed)); 254 255 JS::CompileOptions options(cx); 256 options.setFileAndLine("", 0); 257 258 JS::EnvironmentChain emptyEnvChain(cx, JS::SupportUnscopables::No); 259 fun = JS::CompileFunction(cx, emptyEnvChain, options, "untrusted", 1, 260 ¶mName, srcBuf); 261 CHECK(fun); 262 CHECK(JS_DefineProperty(cx, global, "untrusted", fun, JSPROP_ENUMERATE)); 263 264 JS::RootedValue arg(cx, JS::ObjectValue(*callTrusted)); 265 JS::RootedValue rval(cx); 266 CHECK(JS_CallFunction(cx, nullptr, fun, JS::HandleValueArray(arg), &rval)); 267 CHECK(rval.toInt32() == 42); 268 } 269 270 return true; 271 } 272 END_TEST(testChromeBuffer)