tor-browser

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

Wrapper.cpp (17259B)


      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 "js/Wrapper.h"
      8 
      9 #include "jsexn.h"
     10 
     11 #include "js/CallAndConstruct.h"      // JS::Construct, JS::IsConstructor
     12 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     13 #include "js/friend/WindowProxy.h"    // js::IsWindowProxy
     14 #include "js/Object.h"                // JS::GetBuiltinClass
     15 #include "js/Proxy.h"
     16 #include "vm/Compartment.h"
     17 #include "vm/ErrorObject.h"
     18 #include "vm/Interpreter.h"
     19 #include "vm/JSContext.h"
     20 #include "vm/ProxyObject.h"
     21 #include "vm/Realm.h"
     22 #include "vm/RegExpObject.h"
     23 #include "vm/WrapperObject.h"
     24 
     25 #include "gc/Marking-inl.h"
     26 #include "vm/JSObject-inl.h"
     27 #include "vm/NativeObject-inl.h"
     28 
     29 using namespace js;
     30 
     31 bool Wrapper::finalizeInBackground(const Value& priv) const {
     32  if (!priv.isObject()) {
     33    return true;
     34  }
     35 
     36  /*
     37   * Make the 'background-finalized-ness' of the wrapper the same as the
     38   * wrapped object, to allow transplanting between them.
     39   */
     40  JSObject* wrapped = MaybeForwarded(&priv.toObject());
     41  gc::AllocKind wrappedKind;
     42  if (IsInsideNursery(wrapped)) {
     43    JSRuntime* rt = wrapped->runtimeFromMainThread();
     44    wrappedKind = wrapped->allocKindForTenure(rt->gc.nursery());
     45  } else {
     46    wrappedKind = wrapped->asTenured().getAllocKind();
     47  }
     48  return IsBackgroundFinalized(wrappedKind);
     49 }
     50 
     51 bool ForwardingProxyHandler::getOwnPropertyDescriptor(
     52    JSContext* cx, HandleObject proxy, HandleId id,
     53    MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const {
     54  assertEnteredPolicy(cx, proxy, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
     55  RootedObject target(cx, proxy->as<ProxyObject>().target());
     56  return GetOwnPropertyDescriptor(cx, target, id, desc);
     57 }
     58 
     59 bool ForwardingProxyHandler::defineProperty(JSContext* cx, HandleObject proxy,
     60                                            HandleId id,
     61                                            Handle<PropertyDescriptor> desc,
     62                                            ObjectOpResult& result) const {
     63  assertEnteredPolicy(cx, proxy, id, SET);
     64  RootedObject target(cx, proxy->as<ProxyObject>().target());
     65  return DefineProperty(cx, target, id, desc, result);
     66 }
     67 
     68 bool ForwardingProxyHandler::ownPropertyKeys(
     69    JSContext* cx, HandleObject proxy, MutableHandleIdVector props) const {
     70  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), ENUMERATE);
     71  RootedObject target(cx, proxy->as<ProxyObject>().target());
     72  return GetPropertyKeys(
     73      cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
     74 }
     75 
     76 bool ForwardingProxyHandler::delete_(JSContext* cx, HandleObject proxy,
     77                                     HandleId id,
     78                                     ObjectOpResult& result) const {
     79  assertEnteredPolicy(cx, proxy, id, SET);
     80  RootedObject target(cx, proxy->as<ProxyObject>().target());
     81  return DeleteProperty(cx, target, id, result);
     82 }
     83 
     84 bool ForwardingProxyHandler::enumerate(JSContext* cx, HandleObject proxy,
     85                                       MutableHandleIdVector props) const {
     86  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), ENUMERATE);
     87  MOZ_ASSERT(
     88      !hasPrototype());  // Should never be called if there's a prototype.
     89  RootedObject target(cx, proxy->as<ProxyObject>().target());
     90  return EnumerateProperties(cx, target, props);
     91 }
     92 
     93 bool ForwardingProxyHandler::getPrototype(JSContext* cx, HandleObject proxy,
     94                                          MutableHandleObject protop) const {
     95  RootedObject target(cx, proxy->as<ProxyObject>().target());
     96  return GetPrototype(cx, target, protop);
     97 }
     98 
     99 bool ForwardingProxyHandler::setPrototype(JSContext* cx, HandleObject proxy,
    100                                          HandleObject proto,
    101                                          ObjectOpResult& result) const {
    102  RootedObject target(cx, proxy->as<ProxyObject>().target());
    103  return SetPrototype(cx, target, proto, result);
    104 }
    105 
    106 bool ForwardingProxyHandler::getPrototypeIfOrdinary(
    107    JSContext* cx, HandleObject proxy, bool* isOrdinary,
    108    MutableHandleObject protop) const {
    109  RootedObject target(cx, proxy->as<ProxyObject>().target());
    110  return GetPrototypeIfOrdinary(cx, target, isOrdinary, protop);
    111 }
    112 
    113 bool ForwardingProxyHandler::setImmutablePrototype(JSContext* cx,
    114                                                   HandleObject proxy,
    115                                                   bool* succeeded) const {
    116  RootedObject target(cx, proxy->as<ProxyObject>().target());
    117  return SetImmutablePrototype(cx, target, succeeded);
    118 }
    119 
    120 bool ForwardingProxyHandler::preventExtensions(JSContext* cx,
    121                                               HandleObject proxy,
    122                                               ObjectOpResult& result) const {
    123  RootedObject target(cx, proxy->as<ProxyObject>().target());
    124  return PreventExtensions(cx, target, result);
    125 }
    126 
    127 bool ForwardingProxyHandler::isExtensible(JSContext* cx, HandleObject proxy,
    128                                          bool* extensible) const {
    129  RootedObject target(cx, proxy->as<ProxyObject>().target());
    130  return IsExtensible(cx, target, extensible);
    131 }
    132 
    133 bool ForwardingProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id,
    134                                 bool* bp) const {
    135  assertEnteredPolicy(cx, proxy, id, GET);
    136  MOZ_ASSERT(
    137      !hasPrototype());  // Should never be called if there's a prototype.
    138  RootedObject target(cx, proxy->as<ProxyObject>().target());
    139  return HasProperty(cx, target, id, bp);
    140 }
    141 
    142 bool ForwardingProxyHandler::get(JSContext* cx, HandleObject proxy,
    143                                 HandleValue receiver, HandleId id,
    144                                 MutableHandleValue vp) const {
    145  assertEnteredPolicy(cx, proxy, id, GET);
    146  RootedObject target(cx, proxy->as<ProxyObject>().target());
    147  return GetProperty(cx, target, receiver, id, vp);
    148 }
    149 
    150 bool ForwardingProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id,
    151                                 HandleValue v, HandleValue receiver,
    152                                 ObjectOpResult& result) const {
    153  assertEnteredPolicy(cx, proxy, id, SET);
    154  RootedObject target(cx, proxy->as<ProxyObject>().target());
    155  return SetProperty(cx, target, id, v, receiver, result);
    156 }
    157 
    158 bool ForwardingProxyHandler::call(JSContext* cx, HandleObject proxy,
    159                                  const CallArgs& args) const {
    160  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), CALL);
    161  RootedValue target(cx, proxy->as<ProxyObject>().private_());
    162 
    163  InvokeArgs iargs(cx);
    164  if (!FillArgumentsFromArraylike(cx, iargs, args)) {
    165    return false;
    166  }
    167 
    168  return js::Call(cx, target, args.thisv(), iargs, args.rval());
    169 }
    170 
    171 bool ForwardingProxyHandler::construct(JSContext* cx, HandleObject proxy,
    172                                       const CallArgs& args) const {
    173  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), CALL);
    174 
    175  RootedValue target(cx, proxy->as<ProxyObject>().private_());
    176  if (!IsConstructor(target)) {
    177    ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, target,
    178                     nullptr);
    179    return false;
    180  }
    181 
    182  ConstructArgs cargs(cx);
    183  if (!FillArgumentsFromArraylike(cx, cargs, args)) {
    184    return false;
    185  }
    186 
    187  RootedObject obj(cx);
    188  if (!Construct(cx, target, cargs, args.newTarget(), &obj)) {
    189    return false;
    190  }
    191 
    192  args.rval().setObject(*obj);
    193  return true;
    194 }
    195 
    196 bool ForwardingProxyHandler::hasOwn(JSContext* cx, HandleObject proxy,
    197                                    HandleId id, bool* bp) const {
    198  assertEnteredPolicy(cx, proxy, id, GET);
    199  RootedObject target(cx, proxy->as<ProxyObject>().target());
    200  return HasOwnProperty(cx, target, id, bp);
    201 }
    202 
    203 bool ForwardingProxyHandler::getOwnEnumerablePropertyKeys(
    204    JSContext* cx, HandleObject proxy, MutableHandleIdVector props) const {
    205  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), ENUMERATE);
    206  RootedObject target(cx, proxy->as<ProxyObject>().target());
    207  return GetPropertyKeys(cx, target, JSITER_OWNONLY, props);
    208 }
    209 
    210 bool ForwardingProxyHandler::nativeCall(JSContext* cx, IsAcceptableThis test,
    211                                        NativeImpl impl,
    212                                        const CallArgs& args) const {
    213  args.setThis(
    214      ObjectValue(*args.thisv().toObject().as<ProxyObject>().target()));
    215  if (!test(args.thisv())) {
    216    ReportIncompatible(cx, args);
    217    return false;
    218  }
    219 
    220  return CallNativeImpl(cx, impl, args);
    221 }
    222 
    223 bool ForwardingProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
    224                                             ESClass* cls) const {
    225  RootedObject target(cx, proxy->as<ProxyObject>().target());
    226  return JS::GetBuiltinClass(cx, target, cls);
    227 }
    228 
    229 bool ForwardingProxyHandler::isArray(JSContext* cx, HandleObject proxy,
    230                                     JS::IsArrayAnswer* answer) const {
    231  RootedObject target(cx, proxy->as<ProxyObject>().target());
    232  return IsArray(cx, target, answer);
    233 }
    234 
    235 const char* ForwardingProxyHandler::className(JSContext* cx,
    236                                              HandleObject proxy) const {
    237  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), GET);
    238  RootedObject target(cx, proxy->as<ProxyObject>().target());
    239  return GetObjectClassName(cx, target);
    240 }
    241 
    242 JSString* ForwardingProxyHandler::fun_toString(JSContext* cx,
    243                                               HandleObject proxy,
    244                                               bool isToSource) const {
    245  assertEnteredPolicy(cx, proxy, JS::PropertyKey::Void(), GET);
    246  RootedObject target(cx, proxy->as<ProxyObject>().target());
    247  return fun_toStringHelper(cx, target, isToSource);
    248 }
    249 
    250 RegExpShared* ForwardingProxyHandler::regexp_toShared(
    251    JSContext* cx, HandleObject proxy) const {
    252  RootedObject target(cx, proxy->as<ProxyObject>().target());
    253  return RegExpToShared(cx, target);
    254 }
    255 
    256 bool ForwardingProxyHandler::boxedValue_unbox(JSContext* cx, HandleObject proxy,
    257                                              MutableHandleValue vp) const {
    258  RootedObject target(cx, proxy->as<ProxyObject>().target());
    259  return Unbox(cx, target, vp);
    260 }
    261 
    262 bool ForwardingProxyHandler::isCallable(JSObject* obj) const {
    263  JSObject* target = obj->as<ProxyObject>().target();
    264  return target->isCallable();
    265 }
    266 
    267 bool ForwardingProxyHandler::isConstructor(JSObject* obj) const {
    268  JSObject* target = obj->as<ProxyObject>().target();
    269  return target->isConstructor();
    270 }
    271 
    272 JSObject* Wrapper::New(JSContext* cx, JSObject* obj, const Wrapper* handler,
    273                       const WrapperOptions& options) {
    274  // If this is a cross-compartment wrapper allocate it in the compartment's
    275  // first global. See Compartment::globalForNewCCW.
    276  mozilla::Maybe<AutoRealm> ar;
    277  if (handler->isCrossCompartmentWrapper()) {
    278    ar.emplace(cx, &cx->compartment()->globalForNewCCW());
    279  }
    280  RootedValue priv(cx, ObjectValue(*obj));
    281  return NewProxyObject(cx, handler, priv, options.proto(), options);
    282 }
    283 
    284 JSObject* Wrapper::Renew(JSObject* existing, JSObject* obj,
    285                         const Wrapper* handler) {
    286  existing->as<ProxyObject>().renew(handler, ObjectValue(*obj));
    287  return existing;
    288 }
    289 
    290 JSObject* Wrapper::wrappedObject(JSObject* wrapper) {
    291  MOZ_ASSERT(wrapper->is<WrapperObject>());
    292  JSObject* target = wrapper->as<ProxyObject>().target();
    293 
    294  if (target) {
    295    // A cross-compartment wrapper should never wrap a CCW. We rely on this
    296    // in the wrapper handlers (we use AutoRealm on our return value, and
    297    // AutoRealm cannot be used with CCWs).
    298    MOZ_ASSERT_IF(IsCrossCompartmentWrapper(wrapper),
    299                  !IsCrossCompartmentWrapper(target));
    300 
    301 #ifdef DEBUG
    302    // An incremental GC will eventually mark the targets of black wrappers
    303    // black but while it is in progress we can observe gray targets.
    304    if (!wrapper->runtimeFromMainThread()->gc.isIncrementalGCInProgress() &&
    305        wrapper->isMarkedBlack()) {
    306      JS::AssertObjectIsNotGray(target);
    307    }
    308 #endif
    309 
    310    // Unmark wrapper targets that should be black in case an incremental GC
    311    // hasn't marked them the correct color yet.
    312    JS::ExposeObjectToActiveJS(target);
    313  }
    314 
    315  return target;
    316 }
    317 
    318 JS_PUBLIC_API JSObject* js::UncheckedUnwrapWithoutExpose(JSObject* wrapped) {
    319  while (true) {
    320    if (!wrapped->is<WrapperObject>() || MOZ_UNLIKELY(IsWindowProxy(wrapped))) {
    321      break;
    322    }
    323    wrapped = wrapped->as<WrapperObject>().target();
    324 
    325    // This can be called from when getting a weakmap key delegate() on a
    326    // wrapper whose referent has been moved while it is still unmarked.
    327    if (wrapped) {
    328      wrapped = MaybeForwarded(wrapped);
    329    }
    330  }
    331  return wrapped;
    332 }
    333 
    334 JS_PUBLIC_API JSObject* js::UncheckedUnwrap(JSObject* wrapped,
    335                                            bool stopAtWindowProxy,
    336                                            unsigned* flagsp) {
    337  MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
    338  MOZ_ASSERT(CurrentThreadCanAccessRuntime(wrapped->runtimeFromAnyThread()));
    339 
    340  unsigned flags = 0;
    341  while (true) {
    342    if (!wrapped->is<WrapperObject>() ||
    343        MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(wrapped))) {
    344      break;
    345    }
    346    flags |= Wrapper::wrapperHandler(wrapped)->flags();
    347    wrapped = Wrapper::wrappedObject(wrapped);
    348  }
    349  if (flagsp) {
    350    *flagsp = flags;
    351  }
    352  return wrapped;
    353 }
    354 
    355 JS_PUBLIC_API JSObject* js::CheckedUnwrapStatic(JSObject* obj) {
    356  while (true) {
    357    JSObject* wrapper = obj;
    358    obj = UnwrapOneCheckedStatic(obj);
    359    if (!obj || obj == wrapper) {
    360      return obj;
    361    }
    362  }
    363 }
    364 
    365 JS_PUBLIC_API JSObject* js::UnwrapOneCheckedStatic(JSObject* obj) {
    366  MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
    367  MOZ_ASSERT(CurrentThreadCanAccessRuntime(obj->runtimeFromAnyThread()));
    368 
    369  // Note: callers that care about WindowProxy unwrapping should use
    370  // CheckedUnwrapDynamic or UnwrapOneCheckedDynamic instead of this. We don't
    371  // unwrap WindowProxy here to preserve legacy behavior and for consistency
    372  // with CheckedUnwrapDynamic's default stopAtWindowProxy = true.
    373  if (!obj->is<WrapperObject>() || MOZ_UNLIKELY(IsWindowProxy(obj))) {
    374    return obj;
    375  }
    376 
    377  const Wrapper* handler = Wrapper::wrapperHandler(obj);
    378  return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
    379 }
    380 
    381 JS_PUBLIC_API JSObject* js::CheckedUnwrapDynamic(JSObject* obj, JSContext* cx,
    382                                                 bool stopAtWindowProxy) {
    383  RootedObject wrapper(cx, obj);
    384  while (true) {
    385    JSObject* unwrapped =
    386        UnwrapOneCheckedDynamic(wrapper, cx, stopAtWindowProxy);
    387    if (!unwrapped || unwrapped == wrapper) {
    388      return unwrapped;
    389    }
    390    wrapper = unwrapped;
    391  }
    392 }
    393 
    394 JS_PUBLIC_API JSObject* js::UnwrapOneCheckedDynamic(HandleObject obj,
    395                                                    JSContext* cx,
    396                                                    bool stopAtWindowProxy) {
    397  MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
    398  MOZ_ASSERT(CurrentThreadCanAccessRuntime(obj->runtimeFromAnyThread()));
    399  // We should know who's asking.
    400  MOZ_ASSERT(cx);
    401  MOZ_ASSERT(cx->realm());
    402 
    403  if (!obj->is<WrapperObject>() ||
    404      MOZ_UNLIKELY(stopAtWindowProxy && IsWindowProxy(obj))) {
    405    return obj;
    406  }
    407 
    408  const Wrapper* handler = Wrapper::wrapperHandler(obj);
    409  if (!handler->hasSecurityPolicy() ||
    410      handler->dynamicCheckedUnwrapAllowed(obj, cx)) {
    411    return Wrapper::wrappedObject(obj);
    412  }
    413 
    414  return nullptr;
    415 }
    416 
    417 void js::ReportAccessDenied(JSContext* cx) {
    418  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    419                            JSMSG_OBJECT_ACCESS_DENIED);
    420 }
    421 
    422 const char Wrapper::family = 0;
    423 const Wrapper Wrapper::singleton((unsigned)0);
    424 const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
    425 
    426 // TODO: this is temporarily annotated as MOZ_GLOBINIT rather than
    427 // MOZ_RUNINIT to avoid clang plugin errors when building with
    428 // explicit resource management enabled (bug 1928633)
    429 MOZ_GLOBINIT JSObject* const Wrapper::defaultProto = TaggedProto::LazyProto;
    430 
    431 /* Compartments. */
    432 
    433 JSObject* js::TransparentObjectWrapper(JSContext* cx, HandleObject existing,
    434                                       HandleObject obj) {
    435  // Allow wrapping outer window proxies.
    436  MOZ_ASSERT(!obj->is<WrapperObject>() || IsWindowProxy(obj));
    437  return Wrapper::New(cx, obj, &CrossCompartmentWrapper::singleton);
    438 }
    439 
    440 ErrorCopier::~ErrorCopier() {
    441  JSContext* cx = ar->context();
    442 
    443  // The provenance of Debugger.DebuggeeWouldRun is the topmost locking
    444  // debugger compartment; it should not be copied around.
    445  if (ar->origin()->compartment() != cx->compartment() &&
    446      cx->isExceptionPending() && !cx->isThrowingDebuggeeWouldRun()) {
    447    RootedValue exc(cx);
    448    if (cx->getPendingException(&exc) && exc.isObject() &&
    449        exc.toObject().is<ErrorObject>()) {
    450      Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
    451      cx->clearPendingException();
    452      ar.reset();
    453      Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
    454      if (JSObject* copyobj = CopyErrorObject(cx, errObj)) {
    455        RootedValue rootedCopy(cx, ObjectValue(*copyobj));
    456        cx->setPendingException(rootedCopy, stack);
    457      }
    458    }
    459  }
    460 }