зеркало из https://github.com/mozilla/gecko-dev.git
Bug 982974 - Be paranoid about neutering ArrayBuffer objects. r=sfink, r=jorendorff
This commit is contained in:
Родитель
d63d946fe7
Коммит
4a88e35e6f
|
@ -473,23 +473,28 @@ ArrayBufferObject::changeContents(JSContext *cx, ObjectElements *newHeader)
|
|||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::neuter(JSContext *cx)
|
||||
ArrayBufferObject::neuter(ObjectElements *newHeader, JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(!isSharedArrayBuffer());
|
||||
MOZ_ASSERT(!isSharedArrayBuffer());
|
||||
|
||||
if (hasStealableContents()) {
|
||||
MOZ_ASSERT(newHeader);
|
||||
|
||||
JS_ASSERT(cx);
|
||||
if (hasDynamicElements() && !isAsmJSArrayBuffer()) {
|
||||
ObjectElements *oldHeader = getElementsHeader();
|
||||
changeContents(cx, ObjectElements::fromElements(fixedElements()));
|
||||
MOZ_ASSERT(newHeader != oldHeader);
|
||||
|
||||
changeContents(cx, newHeader);
|
||||
|
||||
FreeOp fop(cx->runtime(), false);
|
||||
fop.free_(oldHeader);
|
||||
} else {
|
||||
elements = newHeader->elements();
|
||||
}
|
||||
|
||||
uint32_t byteLen = 0;
|
||||
updateElementsHeader(getElementsHeader(), byteLen);
|
||||
updateElementsHeader(newHeader, byteLen);
|
||||
|
||||
getElementsHeader()->setIsNeuteredBuffer();
|
||||
newHeader->setIsNeuteredBuffer();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -756,19 +761,20 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp
|
|||
ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
|
||||
uint8_t **data)
|
||||
{
|
||||
// If the ArrayBuffer's elements are dynamically allocated and nothing else
|
||||
// prevents us from stealing them, transfer ownership directly. Otherwise,
|
||||
// the elements are small and allocated inside the ArrayBuffer object's GC
|
||||
// header so we must make a copy.
|
||||
ObjectElements *transferableHeader;
|
||||
bool stolen;
|
||||
if (buffer->hasDynamicElements() && !buffer->isAsmJSArrayBuffer()) {
|
||||
stolen = true;
|
||||
transferableHeader = buffer->getElementsHeader();
|
||||
} else {
|
||||
stolen = false;
|
||||
|
||||
uint32_t byteLen = buffer->byteLength();
|
||||
|
||||
// If the ArrayBuffer's elements are transferrable, transfer ownership
|
||||
// directly. Otherwise we have to copy the data into new elements.
|
||||
ObjectElements *transferableHeader;
|
||||
ObjectElements *newHeader;
|
||||
bool stolen = buffer->hasStealableContents();
|
||||
if (stolen) {
|
||||
transferableHeader = buffer->getElementsHeader();
|
||||
|
||||
newHeader = AllocateArrayBufferContents(cx, byteLen);
|
||||
if (!newHeader)
|
||||
return false;
|
||||
} else {
|
||||
transferableHeader = AllocateArrayBufferContents(cx, byteLen);
|
||||
if (!transferableHeader)
|
||||
return false;
|
||||
|
@ -776,6 +782,9 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
|
|||
initElementsHeader(transferableHeader, byteLen);
|
||||
void *headerDataPointer = reinterpret_cast<void*>(transferableHeader->elements());
|
||||
memcpy(headerDataPointer, buffer->dataPointer(), byteLen);
|
||||
|
||||
// Keep using the current elements.
|
||||
newHeader = buffer->getElementsHeader();
|
||||
}
|
||||
|
||||
JS_ASSERT(!IsInsideNursery(cx->runtime(), transferableHeader));
|
||||
|
@ -787,13 +796,13 @@ ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffe
|
|||
if (!ArrayBufferObject::neuterViews(cx, buffer))
|
||||
return false;
|
||||
|
||||
// If the elements were taken from the neutered buffer, revert it back to
|
||||
// using inline storage so it doesn't attempt to free the stolen elements
|
||||
// when finalized.
|
||||
// If the elements were transferrable, revert the buffer back to using
|
||||
// inline storage so it doesn't attempt to free the stolen elements when
|
||||
// finalized.
|
||||
if (stolen)
|
||||
buffer->changeContents(cx, ObjectElements::fromElements(buffer->fixedElements()));
|
||||
|
||||
buffer->neuter(cx);
|
||||
buffer->neuter(newHeader, cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1270,9 +1279,33 @@ JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj)
|
|||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
|
||||
if (!ArrayBufferObject::neuterViews(cx, buffer))
|
||||
|
||||
ObjectElements *newHeader;
|
||||
if (buffer->hasStealableContents()) {
|
||||
// If we're "disposing" with the buffer contents, allocate zeroed
|
||||
// memory of equal size and swap that in as contents. This ensures
|
||||
// that stale indexes that assume the original length, won't index out
|
||||
// of bounds. This is a temporary hack: when we're confident we've
|
||||
// eradicated all stale accesses, we'll stop doing this.
|
||||
newHeader = AllocateArrayBufferContents(cx, buffer->byteLength());
|
||||
if (!newHeader)
|
||||
return false;
|
||||
buffer->neuter(cx);
|
||||
} else {
|
||||
// This case neuters out the existing elements in-place, so use the
|
||||
// old header as new.
|
||||
newHeader = buffer->getElementsHeader();
|
||||
}
|
||||
|
||||
// Mark all views of the ArrayBuffer as neutered.
|
||||
if (!ArrayBufferObject::neuterViews(cx, buffer)) {
|
||||
if (buffer->hasStealableContents()) {
|
||||
FreeOp fop(cx->runtime(), false);
|
||||
fop.free_(newHeader);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer->neuter(newHeader, cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,24 @@ class ArrayBufferObject : public JSObject
|
|||
static bool saveArrayBufferList(JSCompartment *c, ArrayBufferVector &vector);
|
||||
static void restoreArrayBufferLists(ArrayBufferVector &vector);
|
||||
|
||||
bool hasStealableContents() const {
|
||||
// Inline elements strictly adhere to the corresponding buffer.
|
||||
if (!hasDynamicElements())
|
||||
return false;
|
||||
|
||||
// asm.js buffer contents are transferred by copying, just like inline
|
||||
// elements.
|
||||
if (isAsmJSArrayBuffer())
|
||||
return false;
|
||||
|
||||
// Neutered contents aren't transferrable because we want a neutered
|
||||
// array's contents to be backed by zeroed memory equal in length to
|
||||
// the original buffer contents. Transferring these contents would
|
||||
// allocate new ones based on the current byteLength, which is 0 for a
|
||||
// neutered array -- not the original byteLength.
|
||||
return !isNeutered();
|
||||
}
|
||||
|
||||
static bool stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer, void **contents,
|
||||
uint8_t **data);
|
||||
|
||||
|
@ -176,10 +194,16 @@ class ArrayBufferObject : public JSObject
|
|||
uint8_t * dataPointer() const;
|
||||
|
||||
/*
|
||||
* Discard the ArrayBuffer contents. For asm.js buffers, at least, should
|
||||
* Discard the ArrayBuffer contents, and use |newHeader| for the buffer's
|
||||
* new contents. (These new contents are zeroed, of identical size in
|
||||
* memory as the current contents, but appear to be neutered and of zero
|
||||
* length. This is purely precautionary against stale indexes that were
|
||||
* in-bounds with respect to the initial length but would not be after
|
||||
* neutering. This precaution will be removed once we're sure such stale
|
||||
* indexing no longer happens.) For asm.js buffers, at least, should
|
||||
* be called after neuterViews().
|
||||
*/
|
||||
void neuter(JSContext *cx);
|
||||
void neuter(ObjectElements *newHeader, JSContext *cx);
|
||||
|
||||
/*
|
||||
* Check if the arrayBuffer contains any data. This will return false for
|
||||
|
|
Загрузка…
Ссылка в новой задаче