☠☠ backed out by dee88fe417fe ☠ ☠ | |
author | Bobby Holley <bobbyholley@gmail.com> |
Fri, 22 Feb 2013 08:14:34 -0800 | |
changeset 122669 | 4d301b2bcad047442fa34470893aff52b4df73ee |
parent 122668 | ab07392f24241194e485b42e9c5b598bba5aaf87 |
child 122670 | 8177cabc4e22a9f4f9bdbd241101e020b6bbd62c |
push id | 24356 |
push user | gszorc@mozilla.com |
push date | Sun, 24 Feb 2013 01:00:12 +0000 |
treeherder | mozilla-central@195e706140d1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mrbkap |
bugs | 836301 |
milestone | 22.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -876,16 +876,19 @@ JSRuntime::JSRuntime(JSUseHelperThreads jitHardening(false), ionPcScriptCache(NULL), threadPool(this), ctypesActivityCallback(NULL), parallelWarmup(0), ionReturnOverride_(MagicValue(JS_ARG_POISON)), useHelperThreads_(useHelperThreads), requestedHelperThreadCount(-1), +#ifdef DEBUG + enteredPolicy(NULL), +#endif rngNonce(0) { /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ JS_INIT_CLIST(&onNewGlobalObjectWatchers); PodZero(&debugHooks); PodZero(&atomState);
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -564,17 +564,17 @@ struct MallocProvider } JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE) }; namespace gc { class MarkingValidator; } // namespace gc - +class AutoEnterPolicy; } // namespace js struct JSRuntime : js::RuntimeFriendFields, public js::MallocProvider<JSRuntime> { /* * Per-thread data for the main thread that is associated with * this JSRuntime, as opposed to any worker threads used in @@ -1296,17 +1296,21 @@ struct JSRuntime : js::RuntimeFriendFiel #ifdef JS_THREADSAFE if (requestedHelperThreadCount < 0) return js::GetCPUCount() - 1; return requestedHelperThreadCount; #else return 0; #endif } +#ifdef DEBUG + public: + js::AutoEnterPolicy *enteredPolicy; +#endif private: /* * Used to ensure that compartments created at the same time get different * random number sequences. See js::InitRandom. */ uint64_t rngNonce; public:
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1411,11 +1411,18 @@ class JS_FRIEND_API(AutoCTypesActivityCa void DoEndCallback() { if (callback) { callback(cx, endType); callback = NULL; } } }; +#ifdef DEBUG +extern JS_FRIEND_API(void) +assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id); +#else +inline void assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id) {}; +#endif + } /* namespace js */ #endif /* jsfriendapi_h___ */
--- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -58,16 +58,48 @@ js::AutoEnterPolicy::reportError(JSConte } else { JSString *str = IdToString(cx, id); const jschar *prop = str ? str->getCharsZ(cx) : NULL; JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_PROPERTY_ACCESS_DENIED, prop); } } +#ifdef DEBUG +JS_FRIEND_API(void) +js::AutoEnterPolicy::recordEnter(JSContext *cx, JSObject *proxy, jsid id) +{ + if (allowed()) { + context = cx; + enteredProxy.construct(cx, proxy); + enteredId.construct(cx, id); + prev = cx->runtime->enteredPolicy; + cx->runtime->enteredPolicy = this; + } +} + +JS_FRIEND_API(void) +js::AutoEnterPolicy::recordLeave() +{ + if (!enteredProxy.empty()) { + JS_ASSERT(context->runtime->enteredPolicy == this); + context->runtime->enteredPolicy = prev; + } +} + +JS_FRIEND_API(void) +js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id) +{ + MOZ_ASSERT(proxy->isProxy()); + MOZ_ASSERT(cx->runtime->enteredPolicy); + MOZ_ASSERT(cx->runtime->enteredPolicy->enteredProxy.ref().get() == proxy); + MOZ_ASSERT(cx->runtime->enteredPolicy->enteredId.ref().get() == id); +} +#endif + BaseProxyHandler::BaseProxyHandler(void *family) : mFamily(family), mHasPrototype(false), mHasPolicy(false) { } BaseProxyHandler::~BaseProxyHandler() @@ -80,40 +112,43 @@ BaseProxyHandler::enter(JSContext *cx, J { *bp = true; return true; } bool BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); if (!getPropertyDescriptor(cx, proxy, id, &desc, 0)) return false; *bp = !!desc.obj; return true; } bool BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0)) return false; *bp = !!desc.obj; return true; } bool BaseProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedObject receiver(cx, receiver_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); if (!getPropertyDescriptor(cx, proxy, id, &desc, 0)) return false; if (!desc.obj) { vp->setUndefined(); @@ -145,32 +180,34 @@ bool BaseProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject *receiver_, uint32_t index, Value *vp, bool *present) { RootedObject proxy(cx, proxy_); RootedObject receiver(cx, receiver_); RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; + assertEnteredPolicy(cx, proxy, id); if (!has(cx, proxy, id, present)) return false; if (!*present) { Debug_SetValueRangeToCrashOnTouch(vp, 1); return true; } return get(cx, proxy, receiver, id, vp); } bool BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, bool strict, Value *vp) { + assertEnteredPolicy(cx, proxy_, id_); RootedObject proxy(cx, proxy_), receiver(cx, receiver_); RootedId id(cx, id_); AutoPropertyDescriptorRooter desc(cx); if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, JSRESOLVE_ASSIGNING)) return false; /* The control-flow here differs from ::get() because of the fall-through case below. */ if (desc.obj) { @@ -237,44 +274,47 @@ BaseProxyHandler::set(JSContext *cx, JSO desc.getter = NULL; desc.setter = NULL; // Pick up the class getter/setter. return defineProperty(cx, receiver, id, &desc); } bool BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) { + assertEnteredPolicy(cx, proxyArg, JSID_VOID); JS_ASSERT(props.length() == 0); RootedObject proxy(cx, proxyArg); if (!getOwnPropertyNames(cx, proxy, props)) return false; /* Select only the enumerable properties through in-place iteration. */ AutoPropertyDescriptorRooter desc(cx); size_t i = 0; for (size_t j = 0, len = props.length(); j < len; j++) { JS_ASSERT(i <= j); jsid id = props[j]; + AutoWaivePolicy policy(cx, proxy, id); if (!getOwnPropertyDescriptor(cx, proxy, id, &desc, 0)) return false; if (desc.obj && (desc.attrs & JSPROP_ENUMERATE)) props[i++] = id; } JS_ASSERT(i <= props.length()); props.resize(i); return true; } bool BaseProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, proxy_, JSID_VOID); RootedObject proxy(cx, proxy_); AutoIdVector props(cx); if ((flags & JSITER_OWNONLY) ? !keys(cx, proxy, props) : !enumerate(cx, proxy, props)) { return false; } @@ -286,28 +326,30 @@ BaseProxyHandler::iterate(JSContext *cx, *vp = value; return true; } bool BaseProxyHandler::call(JSContext *cx, JSObject *proxy, unsigned argc, Value *vp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); AutoValueRooter rval(cx); RootedValue call(cx, GetCall(proxy)); JSBool ok = Invoke(cx, vp[1], call, argc, JS_ARGV(cx, vp), rval.addr()); if (ok) JS_SET_RVAL(cx, vp, rval.value()); return ok; } bool BaseProxyHandler::construct(JSContext *cx, JSObject *proxy_, unsigned argc, Value *argv, Value *rval) { + assertEnteredPolicy(cx, proxy_, JSID_VOID); RootedObject proxy(cx, proxy_); RootedValue fval(cx, GetConstruct(proxy_)); if (fval.isUndefined()) fval = GetCall(proxy); return InvokeConstructor(cx, fval, argc, argv, rval); } JSString * @@ -316,16 +358,17 @@ BaseProxyHandler::obj_toString(JSContext return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy) ? "[object Function]" : "[object Object]"); } JSString * BaseProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent) { + assertEnteredPolicy(cx, proxy, JSID_VOID); Value fval = GetCall(proxy); if (IsFunctionProxy(proxy) && (fval.isPrimitive() || !fval.toObject().isFunction())) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, js_Function_str, js_toString_str, "object"); return NULL; @@ -361,16 +404,17 @@ BaseProxyHandler::nativeCall(JSContext * { ReportIncompatible(cx, args); return false; } bool BaseProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedValue val(cx, ObjectValue(*proxy.get())); js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, JSDVG_SEARCH_STACK, val, NullPtr()); return false; } bool BaseProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx) @@ -397,16 +441,18 @@ BaseProxyHandler::getPrototypeOf(JSConte return true; } bool DirectProxyHandler::getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, proxy, id); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, GetProxyTargetObject(proxy)); return JS_GetPropertyDescriptorById(cx, target, id, 0, desc); } static bool GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, jsid id, unsigned flags, JSPropertyDescriptor *desc) { @@ -421,57 +467,63 @@ GetOwnPropertyDescriptor(JSContext *cx, desc->obj = NULL; return true; } bool DirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); return GetOwnPropertyDescriptor(cx, target, id, 0, desc); } bool DirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_, PropertyDescriptor *desc) { + assertEnteredPolicy(cx, proxy, id_); RootedObject obj(cx, GetProxyTargetObject(proxy)); Rooted<jsid> id(cx, id_); Rooted<Value> v(cx, desc->value); return CheckDefineProperty(cx, obj, id, v, desc->getter, desc->setter, desc->attrs) && JS_DefinePropertyById(cx, obj, id, v, desc->getter, desc->setter, desc->attrs); } bool DirectProxyHandler::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedObject target(cx, GetProxyTargetObject(proxy)); return GetPropertyNames(cx, target, JSITER_OWNONLY | JSITER_HIDDEN, &props); } bool DirectProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { RootedValue v(cx); + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_DeletePropertyById2(cx, target, id, v.address())) return false; JSBool b; if (!JS_ValueToBoolean(cx, v, &b)) return false; *bp = !!b; return true; } bool DirectProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. RootedObject target(cx, GetProxyTargetObject(proxy)); return GetPropertyNames(cx, target, 0, &props); } bool DirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) { @@ -483,16 +535,17 @@ DirectProxyHandler::nativeCall(JSContext return CallNativeImpl(cx, impl, args); } bool DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); JSBool b; RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_HasInstance(cx, target, v, &b)) return false; *bp = !!b; return true; } @@ -503,23 +556,25 @@ DirectProxyHandler::objectClassIs(JSObje { RootedObject obj(cx, GetProxyTargetObject(proxy)); return ObjectClassIs(obj, classValue, cx); } JSString * DirectProxyHandler::obj_toString(JSContext *cx, JSObject *proxy) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return obj_toStringHelper(cx, GetProxyTargetObject(proxy)); } JSString * DirectProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, unsigned indent) { + assertEnteredPolicy(cx, proxy, JSID_VOID); RootedObject target(cx, GetProxyTargetObject(proxy)); return fun_toStringHelper(cx, target, indent); } bool DirectProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) { @@ -545,75 +600,83 @@ DirectProxyHandler::weakmapKeyDelegate(J DirectProxyHandler::DirectProxyHandler(void *family) : BaseProxyHandler(family) { } bool DirectProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { + assertEnteredPolicy(cx, proxy, id); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. JSBool found; RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JS_HasPropertyById(cx, target, id, &found)) return false; *bp = !!found; return true; } bool DirectProxyHandler::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp) { + assertEnteredPolicy(cx, proxy, id); RootedObject target(cx, GetProxyTargetObject(proxy)); AutoPropertyDescriptorRooter desc(cx); if (!JS_GetPropertyDescriptorById(cx, target, id, 0, &desc)) return false; *bp = (desc.obj == target); return true; } bool DirectProxyHandler::get(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedObject receiver(cx, receiver_); RootedId id(cx, id_); RootedValue value(cx); RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JSObject::getGeneric(cx, target, receiver, id, &value)) return false; *vp = value; return true; } bool DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg, jsid id_, bool strict, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedId id(cx, id_); Rooted<JSObject*> receiver(cx, receiverArg); RootedValue value(cx, *vp); RootedObject target(cx, GetProxyTargetObject(proxy)); if (!JSObject::setGeneric(cx, target, receiver, id, &value, strict)) return false; *vp = value; return true; } bool DirectProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return GetPropertyNames(cx, GetProxyTargetObject(proxy), JSITER_OWNONLY, &props); } bool DirectProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, proxy, JSID_VOID); + JS_ASSERT(!hasPrototype()); // Should never be called if there's a prototype. Rooted<JSObject*> target(cx, GetProxyTargetObject(proxy)); RootedValue value(cx); if (!GetIterator(cx, target, flags, &value)) return false; *vp = value; return true; }
--- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -343,30 +343,74 @@ NewProxyObject(JSContext *cx, BaseProxyH JSObject * RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv); class AutoEnterPolicy { public: typedef BaseProxyHandler::Action Action; AutoEnterPolicy(JSContext *cx, BaseProxyHandler *handler, JSObject *wrapper, jsid id, Action act, bool mayThrow) +#ifdef DEBUG + : context(NULL) +#endif { allow = handler->hasPolicy() ? handler->enter(cx, wrapper, id, act, &rv) : true; + recordEnter(cx, wrapper, id); if (!allow && !rv && mayThrow) reportError(cx, id); } + virtual ~AutoEnterPolicy() { recordLeave(); } inline bool allowed() { return allow; } inline bool returnValue() { JS_ASSERT(!allowed()); return rv; } protected: + // no-op constructor for subclass + AutoEnterPolicy() +#ifdef DEBUG + : context(NULL) +#endif + {}; void reportError(JSContext *cx, jsid id); bool allow; bool rv; + +#ifdef DEBUG + JSContext *context; + mozilla::Maybe<RootedObject> enteredProxy; + mozilla::Maybe<RootedId> enteredId; + // NB: We explicitly don't track the entered action here, because sometimes + // SET traps do an implicit GET during their implementation, leading to + // spurious assertions. + AutoEnterPolicy *prev; + void recordEnter(JSContext *cx, JSObject *proxy, jsid id); + void recordLeave(); + + friend void assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id); +#else + inline void recordEnter(JSContext *cx, JSObject *proxy, jsid id) {} + inline void recordLeave() {} +#endif + }; +#ifdef DEBUG +class AutoWaivePolicy : public AutoEnterPolicy { +public: + AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) + { + allow = true; + recordEnter(cx, proxy, id); + } +}; +#else +class AutoWaivePolicy { + public: AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) {}; +}; +#endif + } /* namespace js */ extern JS_FRIEND_API(JSObject *) js_InitProxyClass(JSContext *cx, JSHandleObject obj); #endif
--- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -121,64 +121,16 @@ Wrapper::Wrapper(unsigned flags, bool ha { setHasPrototype(hasPrototype); } Wrapper::~Wrapper() { } -bool -Wrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, - jsid id, PropertyDescriptor *desc, unsigned flags) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::getPropertyDescriptor(cx, wrapper, id, desc, flags); -} - -bool -Wrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapperArg, - jsid id, PropertyDescriptor *desc, unsigned flags) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags); -} - -bool -Wrapper::defineProperty(JSContext *cx, JSObject *wrapperArg, jsid id, - PropertyDescriptor *desc) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::defineProperty(cx, wrapper, id, desc); -} - -bool -Wrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapperArg, - AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::getOwnPropertyNames(cx, wrapper, props); -} - -bool -Wrapper::delete_(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::delete_(cx, wrapper, id, bp); -} - -bool -Wrapper::enumerate(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::enumerate(cx, wrapper, props); -} - /* * Ordinarily, the convert trap would require unwrapping. However, the default * implementation of convert, JS_ConvertStub, obtains a default value by calling * the toString/valueOf method on the wrapper, if any. Throwing if we can't unwrap * in this case would be overly conservative. To make matters worse, XPConnect sometimes * installs a custom convert trap that obtains a default value by calling the * toString method on the wrapper. Doing a puncture in this case would be overly * conservative as well. We deal with these anomalies by falling back to the DefaultValue @@ -203,105 +155,16 @@ Wrapper::defaultValue(JSContext *cx, JSO * necessary because the DefaultValue algorithm above operates on the * wrapper, not the wrappee, so we want to delay the decision to switch * compartments until this point. */ AutoCompartment call(cx, wrappedObject(wrapper)); return DirectProxyHandler::defaultValue(cx, wrapper, hint, vp); } -bool -Wrapper::has(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::has(cx, wrapper, id, bp); -} - -bool -Wrapper::hasOwn(JSContext *cx, JSObject *wrapperArg, jsid id, bool *bp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::hasOwn(cx, wrapper, id, bp); -} - -bool -Wrapper::get(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::get(cx, wrapper, receiver, id, vp); -} - -bool -Wrapper::set(JSContext *cx, JSObject *wrapperArg, JSObject *receiver, jsid id, bool strict, - Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::set(cx, wrapper, receiver, id, strict, vp); -} - -bool -Wrapper::keys(JSContext *cx, JSObject *wrapperArg, AutoIdVector &props) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::keys(cx, wrapper, props); -} - -bool -Wrapper::iterate(JSContext *cx, JSObject *wrapperArg, unsigned flags, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - JS_ASSERT(!hasPrototype()); // Should never be called when there's a prototype. - return DirectProxyHandler::iterate(cx, wrapper, flags, vp); -} - -bool -Wrapper::call(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::call(cx, wrapper, argc, vp); -} - -bool -Wrapper::construct(JSContext *cx, JSObject *wrapperArg, unsigned argc, Value *argv, Value *vp) -{ - RootedObject wrapper(cx, wrapperArg); - return DirectProxyHandler::construct(cx, wrapper, argc, argv, vp); -} - -bool -Wrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) -{ - RootedObject wrapper(cx, &args.thisv().toObject()); - // Note - we don't enter a policy here because our security architecture guards - // against nativeCall by overriding the trap itself in the right circumstances. - return DirectProxyHandler::nativeCall(cx, test, impl, args); -} - -bool -Wrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) -{ - return DirectProxyHandler::hasInstance(cx, wrapper, v, bp); -} - -JSString * -Wrapper::obj_toString(JSContext *cx, JSObject *wrapperArg) -{ - RootedObject wrapper(cx, wrapperArg); - JSString *str = DirectProxyHandler::obj_toString(cx, wrapper); - return str; -} - -JSString * -Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) -{ - JSString *str = DirectProxyHandler::fun_toString(cx, wrapper, indent); - return str; -} - Wrapper Wrapper::singleton((unsigned)0); Wrapper Wrapper::singletonWithPrototype((unsigned)0, true); /* Compartments. */ extern JSObject * js::TransparentObjectWrapper(JSContext *cx, JSObject *existing, JSObject *objArg, JSObject *wrappedProtoArg, JSObject *parentArg,
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -61,48 +61,16 @@ class JS_FRIEND_API(Wrapper) : public Di return mFlags; } explicit Wrapper(unsigned flags, bool hasPrototype = false); virtual ~Wrapper(); /* ES5 Harmony fundamental wrapper traps. */ - virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, PropertyDescriptor *desc, - unsigned flags) MOZ_OVERRIDE; - virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, - jsid id, PropertyDescriptor *desc, - unsigned flags) MOZ_OVERRIDE; - virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id, - PropertyDescriptor *desc) MOZ_OVERRIDE; - virtual bool getOwnPropertyNames(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - virtual bool delete_(JSContext *cx, JSObject *wrapper, jsid id, - bool *bp) MOZ_OVERRIDE; - virtual bool enumerate(JSContext *cx, JSObject *wrapper, - AutoIdVector &props) MOZ_OVERRIDE; - - /* ES5 Harmony derived wrapper traps. */ - virtual bool has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; - virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) MOZ_OVERRIDE; - virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, Value *vp) MOZ_OVERRIDE; - virtual bool set(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, bool strict, - Value *vp) MOZ_OVERRIDE; - virtual bool keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) MOZ_OVERRIDE; - virtual bool iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) MOZ_OVERRIDE; - - /* Spidermonkey extensions. */ - virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, Value *vp) MOZ_OVERRIDE; - virtual bool construct(JSContext *cx, JSObject *wrapper, unsigned argc, Value *argv, Value *rval) MOZ_OVERRIDE; - virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, - CallArgs args) MOZ_OVERRIDE; - virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) MOZ_OVERRIDE; - virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; - virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JSObject *wrapper_, JSType hint, Value *vp) MOZ_OVERRIDE; static Wrapper singleton; static Wrapper singletonWithPrototype; static void *getWrapperFamily(); };
--- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -9,16 +9,18 @@ namespace xpc { // from the wrappee's scope. // // One of the reasons for doing this is to allow standard operations like // chromeArray.forEach(..) to Just Work without explicitly listing them in // __exposedProps__. Since proxies don't automatically inherit behavior from // their prototype, we have to instrument the traps to do this manually. ChromeObjectWrapper ChromeObjectWrapper::singleton; +using js::assertEnteredPolicy; + static bool AllowedByBase(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) { MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) == &ChromeObjectWrapper::singleton); bool bp; ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton; return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp); @@ -53,16 +55,17 @@ PropIsFromStandardPrototype(JSContext *c return PropIsFromStandardPrototype(cx, &desc); } bool ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); // First, try a lookup on the base wrapper if permitted. desc->obj = NULL; if (AllowedByBase(cx, wrapper, id, Wrapper::GET) && !ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id, desc, flags)) { return false; } @@ -82,16 +85,17 @@ ChromeObjectWrapper::getPropertyDescript // If not, try doing the lookup on the prototype. MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); return JS_GetPropertyDescriptorById(cx, wrapperProto, id, 0, desc); } bool ChromeObjectWrapper::has(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { + assertEnteredPolicy(cx, wrapper, id); // Try the lookup on the base wrapper if permitted. if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) && !ChromeObjectWrapperBase::has(cx, wrapper, id, bp)) { return false; } // If we found something or have no prototype, we're done. @@ -109,16 +113,17 @@ ChromeObjectWrapper::has(JSContext *cx, *bp = !!desc.obj; return true; } bool ChromeObjectWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) { + assertEnteredPolicy(cx, wrapper, id); vp->setUndefined(); JSPropertyDescriptor desc; // Only call through to the get trap on the underlying object if we're // allowed to see the property, and if what we'll find is not on a standard // prototype. if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) && !PropIsFromStandardPrototype(cx, wrapper, id)) { @@ -150,12 +155,18 @@ bool ChromeObjectWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, bool *bp) { if (AllowedByBase(cx, wrapper, id, act)) return true; // COWs fail silently for GETs, and that also happens to be the only case // where we might want to redirect the lookup to the home prototype chain. *bp = (act == Wrapper::GET); - return *bp && PropIsFromStandardPrototype(cx, wrapper, id); + if (!*bp || id == JSID_VOID) + return false; + + // Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor + // before we've fully entered the policy. Waive our policy. + js::AutoWaivePolicy policy(cx, wrapper, id); + return PropIsFromStandardPrototype(cx, wrapper, id); } }
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -58,60 +58,66 @@ FilterSetter(JSContext *cx, JSObject *wr return true; } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); if (!Base::getPropertyDescriptor(cx, wrapper, id, desc, flags)) return false; return FilterSetter<Policy>(cx, wrapper, id, desc); } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags)) return false; return FilterSetter<Policy>(cx, wrapper, id, desc); } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::getOwnPropertyNames(cx, wrapper, props) && Filter<Policy>(cx, wrapper, props); } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::enumerate(cx, wrapper, props) && Filter<Policy>(cx, wrapper, props); } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::keys(cx, wrapper, props) && Filter<Policy>(cx, wrapper, props); } template <typename Base, typename Policy> bool FilteringWrapper<Base, Policy>::iterate(JSContext *cx, JSObject *wrapper, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); // We refuse to trigger the iterator hook across chrome wrappers because // we don't know how to censor custom iterator objects. Instead we trigger // the default proxy iterate trap, which will ask enumerate() for the list // of (censored) ids. return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); } template <typename Base, typename Policy>
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1405,16 +1405,17 @@ DEBUG_CheckXBLLookup(JSContext *cx, JSPr #define DEBUG_CheckXBLLookup(a, b) {} #endif template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper); if (Traits::isResolving(cx, holder, id)) { desc->obj = NULL; return true; } typename Traits::ResolvingIdImpl resolving(wrapper, id); @@ -1523,16 +1524,17 @@ XrayWrapper<Base, Traits>::getPropertyDe return true; } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); JSObject *holder = Traits::singleton.ensureHolder(cx, wrapper); if (Traits::isResolving(cx, holder, id)) { desc->obj = NULL; return true; } typename Traits::ResolvingIdImpl resolving(wrapper, id); @@ -1569,16 +1571,17 @@ XrayWrapper<Base, Traits>::getOwnPropert return true; } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, js::PropertyDescriptor *desc) { + assertEnteredPolicy(cx, wrapper, id); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, id)) { JSObject *obj = Traits::getTargetObject(wrapper); JSAutoCompartment ac(cx, obj); if (!JS_WrapPropertyDescriptor(cx, desc)) return false; return JS_DefinePropertyById(cx, obj, id, desc->value, desc->getter, desc->setter, @@ -1621,23 +1624,25 @@ XrayWrapper<Base, Traits>::definePropert wrappedDesc.attrs); } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, JS::AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::delete_(JSContext *cx, JSObject *wrapper, jsid id, bool *bp) { + assertEnteredPolicy(cx, wrapper, id); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, id)) { JSObject *obj = Traits::getTargetObject(wrapper); JSAutoCompartment ac(cx, obj); JSBool b; jsval v; @@ -1664,16 +1669,17 @@ XrayWrapper<Base, Traits>::delete_(JSCon return true; } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::enumerate(JSContext *cx, JSObject *wrapper, unsigned flags, JS::AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); // Redirect access straight to the wrapper if we should be transparent. if (XrayUtils::IsTransparent(cx, wrapper, JSID_VOID)) { JSObject *obj = Traits::getTargetObject(wrapper); JSAutoCompartment ac(cx, obj); return js::GetPropertyNames(cx, obj, flags, &props); } if (!AccessCheck::wrapperSubsumes(wrapper)) { @@ -1757,24 +1763,26 @@ XrayWrapper<Base, Traits>::iterate(JSCon // Skip our Base if it isn't already ProxyHandler. return BaseProxyHandler::iterate(cx, wrapper, flags, vp); } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Traits::call(cx, wrapper, argc, vp); } template <typename Base, typename Traits> bool XrayWrapper<Base, Traits>::construct(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *argv, js::Value *rval) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Traits::construct(cx, wrapper, argc, argv, rval); } /* * The Permissive / Security variants should be used depending on whether the * compartment of the wrapper is guranteed to subsume the compartment of the * wrapped object (i.e. - whether it is safe from a security perspective to * unwrap the wrapper).