Backout 7acb1edc3f91 (bug 1244956) for regressing tpaint.

--HG--
extra : rebase_source : 1329c1e0b12891c85b5ec327f4ff2cee310dc468
This commit is contained in:
Terrence Cole 2016-02-17 09:00:47 -08:00
parent 14a39eb4fe
commit f41820ee61
5 changed files with 104 additions and 72 deletions

View File

@ -21,7 +21,6 @@
namespace js {
class AutoLockGC;
class NonEscapingWrapperVector;
class VerifyPreTracer;
namespace gc {
@ -590,8 +589,6 @@ class GCRuntime
void removeRoot(Value* vp);
void setMarkStackLimit(size_t limit, AutoLockGC& lock);
void registerStackWrapperVector(NonEscapingWrapperVector* vec);
bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
@ -925,7 +922,6 @@ class GCRuntime
JS::gcreason::Reason reason);
void bufferGrayRoots();
void markCompartments();
void traceStackWrappers(JSTracer* trc);
IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
@ -1027,9 +1023,6 @@ class GCRuntime
RootedValueMap rootsHash;
// The list of currently live stack-stored wrappers.
mozilla::LinkedList<NonEscapingWrapperVector> stackWrappers;
size_t maxMallocBytes;
// An incrementing id used to assign unique ids to cells that require one.

View File

@ -183,6 +183,29 @@ AutoGCRooter::trace(JSTracer* trc)
return;
}
case WRAPPER: {
/*
* We need to use TraceManuallyBarrieredEdge here because we mark
* wrapper roots in every slice. This is because of some rule-breaking
* in RemapAllWrappersForObject; see comment there.
*/
TraceManuallyBarrieredEdge(trc, &static_cast<AutoWrapperRooter*>(this)->value.get(),
"JS::AutoWrapperRooter.value");
return;
}
case WRAPVECTOR: {
AutoWrapperVector::VectorImpl& vector = static_cast<AutoWrapperVector*>(this)->vector;
/*
* We need to use TraceManuallyBarrieredEdge here because we mark
* wrapper roots in every slice. This is because of some rule-breaking
* in RemapAllWrappersForObject; see comment there.
*/
for (WrapperValue* p = vector.begin(); p < vector.end(); p++)
TraceManuallyBarrieredEdge(trc, &p->get(), "js::AutoWrapperVector.vector");
return;
}
case CUSTOM:
static_cast<JS::CustomAutoRooter*>(this)->trace(trc);
return;
@ -200,12 +223,14 @@ AutoGCRooter::traceAll(JSTracer* trc)
traceAllInContext(&*cx, trc);
}
void
GCRuntime::traceStackWrappers(JSTracer* trc)
/* static */ void
AutoGCRooter::traceAllWrappers(JSTracer* trc)
{
for (auto* vec : stackWrappers) {
for (auto& v : *vec)
TraceManuallyBarrieredEdge(trc, &v, "stack wrapper");
for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) {
for (AutoGCRooter* gcr = cx->roots.autoGCRooters_; gcr; gcr = gcr->down) {
if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
gcr->trace(trc);
}
}
}

View File

@ -861,60 +861,81 @@ class ErrorCopier
};
/*
* NonEscapingWrapperVector can be used to safely store unbarriered wrappers
* obtained from the cross-compartment map that do not escape onto the heap.
* AutoWrapperVector and AutoWrapperRooter can be used to store wrappers that
* are obtained from the cross-compartment map. However, these classes should
* not be used if the wrapper will escape. For example, it should not be stored
* in the heap.
*
* The reason that we need this class is complex, but straightforward enough
* with the right background. Specifically, we need to be able to observe
* wrappers in dead compartments without revivifying the compartment.
* For example, if we TransplantObject from a compartment that is becoming
* dead into a new or existing compartment (e.g. the user clicked the back
* button), we do not want to hold the old tab live (particularly if that
* navigation was caused by a misbehaving site).
*
* What this means is that we must skip the barrier when observing the weakly
* held Value. The result of skipping the read barrier means that if we store
* the wrapper on the stack after marking our roots in the first slice and
* continue using it after sweeping, it will get swept out from under us.
* Additionally, it means that if we leak the wrapper onto the heap, it will
* get held live, but not be present in the wrapper map. In this case a future
* compartmental GC can sweep the thing while it is live on the heap, leading
* to a UAF, sec-crits, and crashes.
*
* The mechanism the NonEscapingWrapperVector uses to prevent the above badness
* from happening is to mark all such wrappers at every GC slice. This can keep
* a compartment live for longer than we'd like, but only in the (hopefully
* rare) case that a GC happens while we are manipulating the wrapper.
* The AutoWrapper rooters are different from other autorooters because their
* wrappers are marked on every GC slice rather than just the first one. If
* there's some wrapper that we want to use temporarily without causing it to be
* marked, we can use these AutoWrapper classes. If we get unlucky and a GC
* slice runs during the code using the wrapper, the GC will mark the wrapper so
* that it doesn't get swept out from under us. Otherwise, the wrapper needn't
* be marked. This is useful in functions like JS_TransplantObject that
* manipulate wrappers in compartments that may no longer be alive.
*/
class MOZ_RAII NonEscapingWrapperVector
: public mozilla::LinkedListElement<NonEscapingWrapperVector>
/*
* This class stores the data for AutoWrapperVector and AutoWrapperRooter. It
* should not be used in any other situations.
*/
struct WrapperValue
{
using Vec = js::GCVector<Value, 1, RuntimeAllocPolicy>;
Vec storage;
/*
* We use unsafeGet() in the constructors to avoid invoking a read barrier
* on the wrapper, which may be dead (see the comment about bug 803376 in
* jsgc.cpp regarding this). If there is an incremental GC while the wrapper
* is in use, the AutoWrapper rooter will ensure the wrapper gets marked.
*/
explicit WrapperValue(const WrapperMap::Ptr& ptr)
: value(*ptr->value().unsafeGet())
{}
explicit WrapperValue(const WrapperMap::Enum& e)
: value(*e.front().value().unsafeGet())
{}
Value& get() { return value; }
Value get() const { return value; }
operator const Value&() const { return value; }
JSObject& toObject() const { return value.toObject(); }
private:
Value value;
};
class MOZ_RAII AutoWrapperVector : public JS::AutoVectorRooterBase<WrapperValue>
{
public:
explicit NonEscapingWrapperVector(JSRuntime* rt) : storage(rt) {
rt->gc.registerStackWrapperVector(this);
explicit AutoWrapperVector(JSContext* cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoVectorRooterBase<WrapperValue>(cx, WRAPVECTOR)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
bool reserve(size_t aRequest) { return storage.reserve(aRequest); }
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
Value& operator[](size_t aIndex) { return storage[aIndex]; }
Value* begin() { return storage.begin(); }
Value* end() { return storage.end(); }
class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
public:
AutoWrapperRooter(JSContext* cx, WrapperValue v
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::AutoGCRooter(cx, WRAPPER), value(v)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
// Avoid the barrier, for the reason described above. See bug 803376 and
// the comment about said bug in jsgc.cpp.
void infallibleAppend(const WrapperMap::Enum& e) {
storage.infallibleAppend(*e.front().value().unsafeGet());
}
void infallibleAppend(const WrapperMap::Ptr& p) {
storage.infallibleAppend(*p->value().unsafeGet());
}
bool append(const WrapperMap::Enum& e) {
return storage.append(*e.front().value().unsafeGet());
operator JSObject*() const {
return value.get().toObjectOrNull();
}
friend void JS::AutoGCRooter::trace(JSTracer* trc);
private:
WrapperValue value;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} /* namespace js */

View File

@ -1787,12 +1787,6 @@ GCRuntime::removeRoot(Value* vp)
poke();
}
void
GCRuntime::registerStackWrapperVector(NonEscapingWrapperVector* vec)
{
stackWrappers.insertBack(vec);
}
extern JS_FRIEND_API(bool)
js::AddRawValueRoot(JSContext* cx, Value* vp, const char* name)
{
@ -6114,7 +6108,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
MOZ_FALLTHROUGH;
case MARK:
traceStackWrappers(&marker);
AutoGCRooter::traceAllWrappers(&marker);
/* If we needed delayed marking for gray roots, then collect until done. */
if (!hasBufferedGrayRoots()) {

View File

@ -470,9 +470,8 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
if (k.kind != CrossCompartmentKey::ObjectWrapper)
continue;
NonEscapingWrapperVector wobj(cx->runtime());
MOZ_ALWAYS_TRUE(wobj.append(e));
JSObject* wrapped = UncheckedUnwrap(&wobj[0].toObject());
AutoWrapperRooter wobj(cx, WrapperValue(e));
JSObject* wrapped = UncheckedUnwrap(wobj);
if (nukeReferencesToWindow == DontNukeWindowReferences &&
IsWindowProxy(wrapped))
@ -483,7 +482,7 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
if (targetFilter.match(wrapped->compartment())) {
// We found a wrapper to nuke.
e.removeFront();
NukeCrossCompartmentWrapper(cx, &wobj[0].toObject());
NukeCrossCompartmentWrapper(cx, wobj);
}
}
}
@ -566,18 +565,18 @@ js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
RootedValue origv(cx, ObjectValue(*oldTargetArg));
RootedObject newTarget(cx, newTargetArg);
NonEscapingWrapperVector toTransplant(cx->runtime());
AutoWrapperVector toTransplant(cx);
if (!toTransplant.reserve(cx->runtime()->numCompartments))
return false;
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.infallibleAppend(wp);
toTransplant.infallibleAppend(WrapperValue(wp));
}
}
for (const Value& v : toTransplant)
for (const WrapperValue& v : toTransplant)
RemapWrapper(cx, &v.toObject(), newTarget);
return true;
@ -587,7 +586,7 @@ JS_FRIEND_API(bool)
js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
const CompartmentFilter& targetFilter)
{
NonEscapingWrapperVector toRecompute(cx->runtime());
AutoWrapperVector toRecompute(cx);
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
// Filter by source compartment.
@ -606,13 +605,13 @@ js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
continue;
// Add it to the list.
if (!toRecompute.append(e))
if (!toRecompute.append(WrapperValue(e)))
return false;
}
}
// Recompute all the wrappers in the list.
for (const Value& v : toRecompute) {
for (const WrapperValue& v : toRecompute) {
JSObject* wrapper = &v.toObject();
JSObject* wrapped = Wrapper::wrappedObject(wrapper);
RemapWrapper(cx, wrapper, wrapped);