Bug 782337 - Stack clobbering (r=bhackett)

This commit is contained in:
Bill McCloskey 2012-08-16 14:03:18 -07:00
parent ba6a1d1769
commit fa9a111172
4 changed files with 45 additions and 19 deletions

View File

@ -0,0 +1,16 @@
gc();
function recur(n)
{
if (n == 0) {
gcslice();
gc();
} else {
recur(n-1);
}
var obj = new Object();
}
validategc(false);
gcslice(0);
recur(10);

View File

@ -2551,7 +2551,7 @@ MarkRuntime(JSTracer *trc, bool useSavedRoots = false)
mjit::ExpandInlineFrames(c);
#endif
rt->stackSpace.mark(trc);
rt->stackSpace.markAndClobber(trc);
rt->debugScopes->mark(trc);
/* The embedding can register additional roots here. */
@ -3544,6 +3544,13 @@ BeginSweepPhase(JSRuntime *rt)
{
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_COMPARTMENTS);
/*
* Eliminate any garbage values from the VM stack that may have been
* left by the JIT in between incremental GC slices. We need to do this
* before discarding analysis data during JSCompartment::sweep.
*/
rt->stackSpace.markAndClobber(NULL);
bool releaseTypes = ReleaseObservedTypes(rt);
for (CompartmentsIter c(rt); !c.done(); c.next()) {
if (c->isCollecting())

View File

@ -624,13 +624,14 @@ StackSpace::containingSegment(const StackFrame *target) const
}
void
StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
StackSpace::markAndClobberFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
{
Value *slotsBegin = fp->slots();
if (!fp->isScriptFrame()) {
JS_ASSERT(fp->isDummyFrame());
gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
if (trc)
gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
return;
}
@ -639,7 +640,8 @@ StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsby
JSScript *script = fp->script();
if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
if (trc)
gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
return;
}
@ -651,6 +653,7 @@ StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsby
* results are thrown away during the sweeping phase, so we always have at
* least one GC to do this.
*/
JSRuntime *rt = script->compartment()->rt;
analyze::AutoEnterAnalysis aea(script->compartment());
analyze::ScriptAnalysis *analysis = script->analysis();
uint32_t offset = pc - script->code;
@ -660,8 +663,9 @@ StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsby
/* Will this slot be synced by the JIT? */
if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset)) {
gc::MarkValueRoot(trc, vp, "vm_stack");
} else if (script->compartment()->isDiscardingJitCode(trc)) {
if (trc)
gc::MarkValueRoot(trc, vp, "vm_stack");
} else if (!trc || script->compartment()->isDiscardingJitCode(trc)) {
/*
* If we're throwing away analysis information, we need to replace
* non-live Values with ones that can safely be marked in later
@ -686,7 +690,7 @@ StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsby
else if (type == JSVAL_TYPE_BOOLEAN)
*vp = BooleanValue(false);
else if (type == JSVAL_TYPE_STRING)
*vp = StringValue(trc->runtime->atomState.nullAtom);
*vp = StringValue(rt->atomState.nullAtom);
else if (type == JSVAL_TYPE_NULL)
*vp = NullValue();
else if (type == JSVAL_TYPE_OBJECT)
@ -695,17 +699,13 @@ StackSpace::markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsby
}
}
gc::MarkValueRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
if (trc)
gc::MarkValueRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
}
void
StackSpace::mark(JSTracer *trc)
StackSpace::markAndClobber(JSTracer *trc)
{
/*
* JIT code can leave values in an incoherent (i.e., unsafe for precise
* marking) state, hence MarkStackRangeConservatively.
*/
/* NB: this depends on the continuity of segments in memory. */
Value *nextSegEnd = firstUnused();
for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
@ -723,16 +723,18 @@ StackSpace::mark(JSTracer *trc)
jsbytecode *pc = seg->maybepc();
for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
/* Mark from fp->slots() to slotsEnd. */
markFrameValues(trc, fp, slotsEnd, pc);
markAndClobberFrame(trc, fp, slotsEnd, pc);
fp->mark(trc);
if (trc)
fp->mark(trc);
slotsEnd = (Value *)fp;
InlinedSite *site;
pc = fp->prevpc(&site);
JS_ASSERT_IF(fp->prev(), !site);
}
gc::MarkValueRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
if (trc)
gc::MarkValueRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
nextSegEnd = (Value *)seg;
}
}

View File

@ -1363,6 +1363,8 @@ class StackSpace
return (Value *)fp >= base_ && (Value *)fp <= trustedEnd_;
}
void markAndClobberFrame(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
public:
StackSpace();
bool init();
@ -1414,8 +1416,7 @@ class StackSpace
bool tryBumpLimit(JSContext *cx, Value *from, unsigned nvals, Value **limit);
/* Called during GC: mark segments, frames, and slots under firstUnused. */
void mark(JSTracer *trc);
void markFrameValues(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
void markAndClobber(JSTracer *trc);
/* Called during GC: sets active flag on compartments with active frames. */
void markActiveCompartments();