diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e80ba6ab9..179aeadf9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2396,9 +2396,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, // transplanting code, since it has no good way to handle errors. This uses // the untrusted script limit, which is not strictly necessary since no // actual script should run. - bool overrecursed = false; - JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, overrecursed = true); - if (overrecursed) { + if (MOZ_UNLIKELY(!js::CheckRecursionConservativeDontReport(cx))) { NS_WARNING("Overrecursion in SetNewDocument"); return NS_ERROR_FAILURE; } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index f08cc56d0..3549303c3 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1891,7 +1891,9 @@ ReparentWrapper(JSContext* aCx, JS::Handle aObjArg) // transplanting code, since it has no good way to handle errors. This uses // the untrusted script limit, which is not strictly necessary since no // actual script should run. - JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE); + if (MOZ_UNLIKELY(!js::CheckRecursionConservative(aCx))) { + return NS_ERROR_FAILURE; + } JS::Rooted aObj(aCx, aObjArg); const DOMJSClass* domClass = GetDOMClass(aObj); diff --git a/js/src/irregexp/RegExpEngine.cpp b/js/src/irregexp/RegExpEngine.cpp index 69da8912c..fb9365209 100644 --- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -28,6 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include "jscntxtinlines.h" + #include "irregexp/RegExpEngine.h" #include "irregexp/NativeRegExpMacroAssembler.h" diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 89bf28635..134fa15e4 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -149,6 +149,8 @@ class ExclusiveContext : public ContextFriendFields, return isJSContext(); } + JSRuntime* ecRuntime() const { return runtime_; } // TenFourFox issue 391 + bool runtimeMatches(JSRuntime* rt) const { return runtime_ == rt; } diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 073a04c7b..cd5bdc1ae 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -19,8 +19,130 @@ #include "vm/ProxyObject.h" #include "vm/Symbol.h" +MOZ_ALWAYS_INLINE bool +JSContext::runningWithTrustedPrincipals() const +{ + return !compartment() || compartment()->principals() == runtime()->trustedPrincipals(); +} + namespace js { +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) +{ + PerThreadDataFriendFields* mainThread = + PerThreadDataFriendFields::getMainThread(GetRuntime(cx)); + uintptr_t limit = mainThread->nativeStackLimit[kind]; +#if JS_STACK_GROWTH_DIRECTION > 0 + limit += extraAllowance; +#else + limit -= extraAllowance; +#endif + return limit; +} + +// Needed for issue 391 -- see below +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(ExclusiveContext* cx, StackKind kind, int extraAllowance = 0) +{ + PerThreadDataFriendFields* mainThread = + PerThreadDataFriendFields::getMainThread(cx->ecRuntime()); + uintptr_t limit = mainThread->nativeStackLimit[kind]; +#if JS_STACK_GROWTH_DIRECTION > 0 + limit += extraAllowance; +#else + limit -= extraAllowance; +#endif + return limit; +} + +MOZ_ALWAYS_INLINE uintptr_t +GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) +{ + StackKind kind = cx->runningWithTrustedPrincipals() ? StackForTrustedScript + : StackForUntrustedScript; + return GetNativeStackLimit(cx, kind, extraAllowance); +} + +/* + * These macros report a stack overflow and run |onerror| if we are close to + * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a + * little extra space so that we can ensure that crucial code is able to run. + * JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check, + * including a safety buffer (as in, it uses the untrusted limit and subtracts + * a little more from it). + */ + +// Implement a fast path a la bug 1342439, but without all that churn. +// Leave the old limit versions here just in case. +// TenFourFox issue 391 + +#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \ + JS_BEGIN_MACRO \ + int stackDummy_; \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx, js::StackForUntrustedScript), &stackDummy_))) { \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), &stackDummy_))) {\ + onerror; \ + } \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ + JS_BEGIN_MACRO \ + if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ + js::ReportOverRecursed(cx); \ + onerror; \ + } \ + JS_END_MACRO + +#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + +#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \ + JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \ + js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ + onerror) + class CompartmentChecker { JSCompartment* compartment; @@ -382,12 +504,6 @@ JSContext::setPendingException(js::Value v) MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment()); } -inline bool -JSContext::runningWithTrustedPrincipals() const -{ - return !compartment() || compartment()->principals() == runtime()->trustedPrincipals(); -} - inline void js::ExclusiveContext::enterCompartment(JSCompartment* c) { diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a808545eb..0ad807e81 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -385,9 +385,24 @@ js::IsObjectInContextCompartment(JSObject* obj, const JSContext* cx) } JS_FRIEND_API(bool) -js::RunningWithTrustedPrincipals(JSContext* cx) +js::CheckRecursion(JSContext* cx) { - return cx->runningWithTrustedPrincipals(); + JS_CHECK_RECURSION(cx, return false); + return true; +} + +JS_FRIEND_API(bool) +js::CheckRecursionConservative(JSContext* cx) +{ + JS_CHECK_RECURSION_CONSERVATIVE(cx, return false); + return true; +} + +JS_FRIEND_API(bool) +js::CheckRecursionConservativeDontReport(JSContext* cx) +{ + JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, return false); + return true; } JS_FRIEND_API(JSFunction*) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 5ca11ac6d..41f73b8bd 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -967,89 +967,13 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */ JS_FRIEND_API(bool) -RunningWithTrustedPrincipals(JSContext* cx); +CheckRecursion(JSContext* cx); -MOZ_ALWAYS_INLINE uintptr_t -GetNativeStackLimit(JSContext* cx, StackKind kind, int extraAllowance = 0) -{ - PerThreadDataFriendFields* mainThread = - PerThreadDataFriendFields::getMainThread(GetRuntime(cx)); - uintptr_t limit = mainThread->nativeStackLimit[kind]; -#if JS_STACK_GROWTH_DIRECTION > 0 - limit += extraAllowance; -#else - limit -= extraAllowance; -#endif - return limit; -} +JS_FRIEND_API(bool) +CheckRecursionConservative(JSContext* cx); -MOZ_ALWAYS_INLINE uintptr_t -GetNativeStackLimit(JSContext* cx, int extraAllowance = 0) -{ - StackKind kind = RunningWithTrustedPrincipals(cx) ? StackForTrustedScript - : StackForUntrustedScript; - return GetNativeStackLimit(cx, kind, extraAllowance); -} - -/* - * These macros report a stack overflow and run |onerror| if we are close to - * using up the C stack. The JS_CHECK_CHROME_RECURSION variant gives us a - * little extra space so that we can ensure that crucial code is able to run. - * JS_CHECK_RECURSION_CONSERVATIVE allows less space than any other check, - * including a safety buffer (as in, it uses the untrusted limit and subtracts - * a little more from it). - */ - -#define JS_CHECK_RECURSION_LIMIT(cx, limit, onerror) \ - JS_BEGIN_MACRO \ - int stackDummy_; \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ - js::ReportOverRecursed(cx); \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx), onerror) - -#define JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, limit, onerror) \ - JS_BEGIN_MACRO \ - int stackDummy_; \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(limit, &stackDummy_))) { \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION_DONT_REPORT(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, js::GetNativeStackLimit(cx), onerror) - -#define JS_CHECK_RECURSION_WITH_SP_DONT_REPORT(cx, sp, onerror) \ - JS_BEGIN_MACRO \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_RECURSION_WITH_SP(cx, sp, onerror) \ - JS_BEGIN_MACRO \ - if (MOZ_UNLIKELY(!JS_CHECK_STACK_SIZE(js::GetNativeStackLimit(cx), sp))) { \ - js::ReportOverRecursed(cx); \ - onerror; \ - } \ - JS_END_MACRO - -#define JS_CHECK_SYSTEM_RECURSION(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, js::GetNativeStackLimit(cx, js::StackForSystemCode), onerror) - -#define JS_CHECK_RECURSION_CONSERVATIVE(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT(cx, \ - js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ - onerror) - -#define JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, onerror) \ - JS_CHECK_RECURSION_LIMIT_DONT_REPORT(cx, \ - js::GetNativeStackLimit(cx, js::StackForUntrustedScript, -1024 * int(sizeof(size_t))), \ - onerror) +JS_FRIEND_API(bool) +CheckRecursionConservativeDontReport(JSContext* cx); JS_FRIEND_API(void) StartPCCountProfiling(JSContext* cx); diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index 4eb8eee7e..d37d0eb01 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -258,7 +258,9 @@ XPCArrayHomogenizer::GetTypeForArray(JSContext* cx, HandleObject array, bool XPCVariant::InitializeData(JSContext* cx) { - JS_CHECK_RECURSION(cx, return false); + if (MOZ_UNLIKELY(!js::CheckRecursion(cx))) { + return false; + } RootedValue val(cx, GetJSVal());