diff --git a/configure.in b/configure.in index 35182a86c87e..e01e33a274b8 100644 --- a/configure.in +++ b/configure.in @@ -2157,8 +2157,9 @@ ia64*-hpux*) # that behavior) that it's better to turn it off. # MSVC warning C4819 warns some UTF-8 characters (e.g. copyright sign) # on non-Western system locales even if it is in a comment. + # khuey says we can safely ignore MSVC warning C4251 CFLAGS="$CFLAGS -wd4819" - CXXFLAGS="$CXXFLAGS -wd4345 -wd4351 -wd4482 -wd4800 -wd4819" + CXXFLAGS="$CXXFLAGS -wd4251 -wd4345 -wd4351 -wd4482 -wd4800 -wd4819" # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" diff --git a/js/src/configure.in b/js/src/configure.in index 2cb27e949433..cfaae2a3d8c9 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1684,7 +1684,8 @@ ia64*-hpux*) CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)" # MSVC warning C4244 is ubiquitous, useless, and annoying. - CXXFLAGS="$CXXFLAGS -wd4244" + # khuey says we can safely ignore MSVC warning C4251 + CXXFLAGS="$CXXFLAGS -wd4244 -wd4251" # make 'foo == bar;' error out CFLAGS="$CFLAGS -we4553" CXXFLAGS="$CXXFLAGS -we4553" diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e6a244b63be0..232ffd03e86d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -881,6 +881,9 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) 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. */ diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 31b9cb6d458c..d3b7f33e26b7 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -569,7 +569,7 @@ struct MallocProvider namespace gc { class MarkingValidator; } // namespace gc - +class AutoEnterPolicy; } // namespace js struct JSRuntime : js::RuntimeFriendFields, @@ -1301,7 +1301,11 @@ struct JSRuntime : js::RuntimeFriendFields, return 0; #endif } +#ifdef DEBUG + public: + js::AutoEnterPolicy *enteredPolicy; +#endif private: /* * Used to ensure that compartments created at the same time get different diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 0fb00e637e2c..e8cf5a0eade5 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1416,6 +1416,13 @@ class JS_FRIEND_API(AutoCTypesActivityCallback) { } }; +#ifdef DEBUG +extern JS_FRIEND_API(void) +assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id); +#else +inline JS_FRIEND_API(void) assertEnteredPolicy(JSContext *cx, JSObject *obj, jsid id) {}; +#endif + } /* namespace js */ #endif /* jsfriendapi_h___ */ diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 6998383c7a42..d344998c2aa3 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -63,6 +63,38 @@ js::AutoEnterPolicy::reportError(JSContext *cx, jsid id) } } +#ifdef DEBUG +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; + } +} + +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), @@ -85,6 +117,7 @@ BaseProxyHandler::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, 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); @@ -97,6 +130,7 @@ BaseProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) 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); @@ -109,6 +143,7 @@ BaseProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp) 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_); @@ -150,6 +185,7 @@ BaseProxyHandler::getElementIfPresent(JSContext *cx, JSObject *proxy_, JSObject RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; + assertEnteredPolicy(cx, proxy, id); if (!has(cx, proxy, id, present)) return false; @@ -166,6 +202,7 @@ 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_); @@ -242,6 +279,7 @@ BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid bool BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) { + assertEnteredPolicy(cx, proxyArg, JSID_VOID); JS_ASSERT(props.length() == 0); RootedObject proxy(cx, proxyArg); @@ -255,6 +293,7 @@ BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) 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)) @@ -270,6 +309,7 @@ BaseProxyHandler::keys(JSContext *cx, JSObject *proxyArg, AutoIdVector &props) bool BaseProxyHandler::iterate(JSContext *cx, JSObject *proxy_, unsigned flags, Value *vp) { + assertEnteredPolicy(cx, proxy_, JSID_VOID); RootedObject proxy(cx, proxy_); AutoIdVector props(cx); @@ -291,6 +331,7 @@ 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()); @@ -303,6 +344,7 @@ 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()) @@ -321,6 +363,7 @@ BaseProxyHandler::obj_toString(JSContext *cx, JSObject *proxy) 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())) { @@ -366,6 +409,7 @@ BaseProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl im 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()); @@ -402,6 +446,8 @@ 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); } @@ -426,6 +472,7 @@ 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); } @@ -434,6 +481,7 @@ bool DirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_, PropertyDescriptor *desc) { + assertEnteredPolicy(cx, proxy, id_); RootedObject obj(cx, GetProxyTargetObject(proxy)); Rooted id(cx, id_); Rooted v(cx, desc->value); @@ -445,6 +493,7 @@ 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); } @@ -453,6 +502,7 @@ 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; @@ -467,6 +517,8 @@ 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); } @@ -488,6 +540,7 @@ 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)) @@ -508,6 +561,7 @@ DirectProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSString * DirectProxyHandler::obj_toString(JSContext *cx, JSObject *proxy) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return obj_toStringHelper(cx, GetProxyTargetObject(proxy)); } @@ -515,6 +569,7 @@ 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); } @@ -550,6 +605,8 @@ DirectProxyHandler::DirectProxyHandler(void *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)) @@ -561,6 +618,7 @@ DirectProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) 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)) @@ -573,6 +631,7 @@ 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); @@ -588,6 +647,7 @@ bool DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg, jsid id_, bool strict, Value *vp) { + assertEnteredPolicy(cx, proxy, id_); RootedId id(cx, id_); Rooted receiver(cx, receiverArg); RootedValue value(cx, *vp); @@ -602,6 +662,7 @@ DirectProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiverArg, bool DirectProxyHandler::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props) { + assertEnteredPolicy(cx, proxy, JSID_VOID); return GetPropertyNames(cx, GetProxyTargetObject(proxy), JSITER_OWNONLY, &props); } @@ -609,6 +670,8 @@ 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 target(cx, GetProxyTargetObject(proxy)); RootedValue value(cx); if (!GetIterator(cx, target, flags, &value)) diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 315fb9b01ebc..b2615df9833f 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -349,22 +349,66 @@ class JS_FRIEND_API(AutoEnterPolicy) 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 enteredProxy; + mozilla::Maybe 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 JS_FRIEND_API(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 JS_FRIEND_API(AutoWaivePolicy) : public AutoEnterPolicy { +public: + AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) + { + allow = true; + recordEnter(cx, proxy, id); + } +}; +#else +class JS_FRIEND_API(AutoWaivePolicy) { + public: AutoWaivePolicy(JSContext *cx, JSObject *proxy, jsid id) {}; +}; +#endif + } /* namespace js */ extern JS_FRIEND_API(JSObject *) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 819b64a8b608..332b4c3ae1ea 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -126,54 +126,6 @@ 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 @@ -208,95 +160,6 @@ Wrapper::defaultValue(JSContext *cx, JSObject *wrapperArg, JSType hint, Value *v 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); diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index db03e9f55f49..c3967026ef69 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -66,38 +66,6 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler 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; diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp index 5868089d5d72..68a84d0dc703 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -14,6 +14,8 @@ namespace xpc { // 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) { @@ -58,6 +60,7 @@ 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) && @@ -87,6 +90,7 @@ ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx, JSObject *wrapper, 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)) @@ -114,6 +118,7 @@ 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 @@ -155,7 +160,13 @@ ChromeObjectWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, // 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); } } diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 69c5a976b484..d8d06841f927 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -63,6 +63,7 @@ bool FilteringWrapper::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(cx, wrapper, id, desc); @@ -74,6 +75,7 @@ FilteringWrapper::getOwnPropertyDescriptor(JSContext *cx, JSObject js::PropertyDescriptor *desc, unsigned flags) { + assertEnteredPolicy(cx, wrapper, id); if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc, flags)) return false; return FilterSetter(cx, wrapper, id, desc); @@ -83,6 +85,7 @@ template bool FilteringWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::getOwnPropertyNames(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -91,6 +94,7 @@ template bool FilteringWrapper::enumerate(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::enumerate(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -99,6 +103,7 @@ template bool FilteringWrapper::keys(JSContext *cx, JSObject *wrapper, AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Base::keys(cx, wrapper, props) && Filter(cx, wrapper, props); } @@ -107,6 +112,7 @@ template bool FilteringWrapper::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 diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 84dcab856fce..f20e04b1b6fa 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1410,6 +1410,7 @@ bool XrayWrapper::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; @@ -1528,6 +1529,7 @@ bool XrayWrapper::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; @@ -1574,6 +1576,7 @@ bool XrayWrapper::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); @@ -1626,6 +1629,7 @@ bool XrayWrapper::getOwnPropertyNames(JSContext *cx, JSObject *wrapper, JS::AutoIdVector &props) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); } @@ -1633,6 +1637,7 @@ template bool XrayWrapper::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); @@ -1669,6 +1674,7 @@ bool XrayWrapper::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); @@ -1762,6 +1768,7 @@ template bool XrayWrapper::call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp) { + assertEnteredPolicy(cx, wrapper, JSID_VOID); return Traits::call(cx, wrapper, argc, vp); } @@ -1770,6 +1777,7 @@ bool XrayWrapper::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); }