tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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