Bug 676937 - Make entering a compartment and pushing a dummy frame an atomic stack operation (r=mrbkap)

This commit is contained in:
Luke Wagner 2011-08-05 14:06:33 -07:00
Родитель d618965c5c
Коммит 6ac38cb8e2
4 изменённых файлов: 53 добавлений и 46 удалений

Просмотреть файл

@ -395,11 +395,12 @@ ForceFrame::enter()
LeaveTrace(context);
JS_ASSERT(context->compartment == target->compartment());
JSCompartment *destination = context->compartment;
JSObject *scopeChain = target->getGlobal();
JS_ASSERT(scopeChain->isNative());
return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame);
return context->stack.pushDummyFrame(context, *scopeChain, frame);
}
AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
@ -428,21 +429,8 @@ AutoCompartment::enter()
JS_ASSERT(scopeChain->isNative());
frame.construct();
/*
* Set the compartment eagerly so that pushDummyFrame associates the
* resource allocation request with 'destination' instead of 'origin'.
* (This is important when content has overflowed the stack and chrome
* is preparing to run JS to throw up a slow script dialog.) However,
* if an exception is thrown, we need it to be in origin's compartment
* so be careful to only report after restoring.
*/
context->compartment = destination;
if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) {
context->compartment = origin;
js_ReportOverRecursed(context);
if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref()))
return false;
}
if (context->isExceptionPending())
context->wrapPendingException();

Просмотреть файл

@ -388,7 +388,8 @@ StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx)
STATIC_POSTCONDITION(!return || ubound(from) >= nvals)
JS_ALWAYS_INLINE bool
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
JSCompartment *dest) const
{
assertInvariants();
JS_ASSERT(from >= firstUnused());
@ -396,10 +397,16 @@ StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptr
JS_ASSERT(from <= commitEnd_);
#endif
if (JS_UNLIKELY(conservativeEnd_ - from < nvals))
return ensureSpaceSlow(cx, report, from, nvals);
return ensureSpaceSlow(cx, report, from, nvals, dest);
return true;
}
bool
StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const
{
return ensureSpace(cx, report, from, nvals, cx->compartment);
}
inline Value *
StackSpace::getStackLimit(JSContext *cx, MaybeReportError report)
{

Просмотреть файл

@ -414,13 +414,13 @@ StackSpace::mark(JSTracer *trc)
}
JS_FRIEND_API(bool)
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
JSCompartment *dest) const
{
assertInvariants();
bool trusted = !cx->compartment ||
cx->compartment->principals == cx->runtime->trustedPrincipals();
JS_ASSERT_IF(dest, cx);
bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
Value *end = trusted ? trustedEnd_ : defaultEnd_;
/*
@ -548,17 +548,17 @@ ContextStack::containsSlow(const StackFrame *target) const
*/
Value *
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg)
MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
{
Value *firstUnused = space().firstUnused();
if (onTop() && extend) {
if (!space().ensureSpace(cx, report, firstUnused, nvars))
if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
return NULL;
return firstUnused;
}
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
return NULL;
FrameRegs *regs;
@ -577,6 +577,13 @@ ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
return seg_->slotsBegin();
}
Value *
ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg)
{
return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment);
}
void
ContextStack::popSegment()
{
@ -696,11 +703,12 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi
}
bool
ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg)
ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg)
{
JSCompartment *dest = scopeChain.compartment();
uintN nvars = VALUES_PER_STACK_FRAME;
Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_);
Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
if (!firstUnused)
return NULL;
@ -708,6 +716,7 @@ ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &s
fp->initDummyFrame(cx, scopeChain);
dfg->regs_.initDummyFrame(*fp);
cx->compartment = dest;
dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
dfg->setPushed(*this);
@ -790,24 +799,11 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
bool
ContextStack::saveFrameChain()
{
/*
* The StackSpace uses the context's current compartment to determine
* whether to allow access to the privileged end-of-stack buffer.
* However, we always want saveFrameChain to have access to this privileged
* buffer since it gets used to prepare calling trusted JS. To force this,
* we clear the current compartment (which is interpreted by ensureSpace as
* 'trusted') and either restore it on OOM or let resetCompartment()
* clobber it.
*/
JSCompartment *original = cx_->compartment;
cx_->compartment = NULL;
JSCompartment *dest = NULL;
bool pushedSeg;
if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) {
cx_->compartment = original;
js_ReportOverRecursed(cx_);
if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
return false;
}
JS_ASSERT(pushedSeg);
JS_ASSERT(!hasfp());

Просмотреть файл

@ -44,6 +44,7 @@
#include "jsfun.h"
struct JSContext;
struct JSCompartment;
namespace js {
@ -1329,10 +1330,15 @@ class StackSpace
friend class ContextStack;
friend class StackFrame;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals,
JSCompartment *dest) const;
inline bool ensureSpace(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
Value *from, ptrdiff_t nvals) const;
Value *from, ptrdiff_t nvals,
JSCompartment *dest) const;
StackSegment &findContainingSegment(const StackFrame *target) const;
public:
@ -1420,6 +1426,9 @@ class ContextStack
/* Implementation details of push* public interface. */
StackSegment *pushSegment(JSContext *cx);
enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg,
JSCompartment *dest);
Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
MaybeExtend extend, bool *pushedSeg);
@ -1502,9 +1511,16 @@ class ContextStack
*/
bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
/* Pushes a "dummy" frame; should be removed one day. */
bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain,
DummyFrameGuard *dfg);
/*
* When changing the compartment of a cx, it is necessary to immediately
* change the scope chain to a global in the right compartment since any
* amount of general VM code can run before the first scripted frame is
* pushed (if at all). This is currently and hackily accomplished by
* pushing a "dummy frame" with the correct scope chain. On success, this
* function will change the compartment to 'scopeChain.compartment()' and
* push a dummy frame for 'scopeChain'. On failure, nothing is changed.
*/
bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg);
/*
* An "inline frame" may only be pushed from within the top, active