mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Backout 7acb1edc3f91 (bug 1244956) for regressing tpaint.
--HG-- extra : rebase_source : 1329c1e0b12891c85b5ec327f4ff2cee310dc468
This commit is contained in:
parent
14a39eb4fe
commit
f41820ee61
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user