Bug 1704771 part 9 - Add recursion limiter. r=jandem

WASI doesn't support catching call-stack overflows with
stack-pointer checks, so custom RecursionLimiter is used to directly count entries to certain functions
and report stack overflows.

Differential Revision: https://phabricator.services.mozilla.com/D111813
This commit is contained in:
Dmitry Bezhetskov 2021-04-22 11:11:12 +00:00
parent 98af369b22
commit a6f2b3ce51
2 changed files with 38 additions and 1 deletions

View File

@ -993,6 +993,14 @@ class RootingContext {
/* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit[StackKindCount];
#ifdef __wasi__
// For WASI we can't catch call-stack overflows with stack-pointer checks, so
// we count recursion depth with RAII based AutoCheckRecursionLimit.
uint32_t wasiRecursionDepth = 0u;
static constexpr uint32_t wasiRecursionDepthLimit = 100u;
#endif // __wasi__
static const RootingContext* get(const JSContext* cx) {
return reinterpret_cast<const RootingContext*>(cx);
}

View File

@ -61,8 +61,26 @@ class MOZ_RAII AutoCheckRecursionLimit {
JS_FRIEND_API bool runningWithTrustedPrincipals(JSContext* cx) const;
#ifdef __wasi__
// The JSContext outlives AutoCheckRecursionLimit so it is safe to use raw
// pointer here.
JSContext* cx_;
#endif // __wasi__
public:
explicit MOZ_ALWAYS_INLINE AutoCheckRecursionLimit(JSContext* cx) {}
explicit MOZ_ALWAYS_INLINE AutoCheckRecursionLimit(JSContext* cx) {
#ifdef __wasi__
cx_ = cx;
++JS::RootingContext::get(cx_)->wasiRecursionDepth;
#endif // __wasi__
}
MOZ_ALWAYS_INLINE ~AutoCheckRecursionLimit() {
#ifdef __wasi__
MOZ_ASSERT(JS::RootingContext::get(cx_)->wasiRecursionDepth > 0);
--JS::RootingContext::get(cx_)->wasiRecursionDepth;
#endif // __wasi__
}
AutoCheckRecursionLimit(const AutoCheckRecursionLimit&) = delete;
void operator=(const AutoCheckRecursionLimit&) = delete;
@ -89,6 +107,17 @@ MOZ_ALWAYS_INLINE bool AutoCheckRecursionLimit::checkLimitImpl(uintptr_t limit,
void* sp) const {
JS_STACK_OOM_POSSIBLY_FAIL();
#ifdef __wasi__
// WASI has two limits:
// 1) The stack pointer in linear memory that grows to zero. See --stack-first
// in js/src/shell/moz.build. 2) The wasiRecursionDepth_ that counts recursion
// depth. Here we should check both.
if (JS::RootingContext::get(cx_)->wasiRecursionDepth >=
JS::RootingContext::wasiRecursionDepthLimit) {
return false;
}
#endif // __wasi__
#if JS_STACK_GROWTH_DIRECTION > 0
return MOZ_LIKELY(uintptr_t(sp) < limit);
#else