Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit c86b9cb

Browse files
author
Lars T Hansen
committed
Bug 1352681 - Make SAB rawBuffer refcounting for structured clone more sophisticated. r=sfink
--HG-- extra : rebase_source : f5c97970013daab78075e2fe68c9704cda7064cd
1 parent d4b0fe7 commit c86b9cb

2 files changed

Lines changed: 118 additions & 16 deletions

File tree

js/public/StructuredClone.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "js/RootingAPI.h"
1818
#include "js/TypeDecls.h"
1919
#include "js/Value.h"
20+
#include "js/Vector.h"
2021

2122
struct JSRuntime;
2223
struct JSStructuredCloneReader;
@@ -188,6 +189,28 @@ enum OwnTransferablePolicy {
188189
NoTransferables
189190
};
190191

192+
namespace js
193+
{
194+
class SharedArrayRawBuffer;
195+
196+
class SharedArrayRawBufferRefs
197+
{
198+
public:
199+
SharedArrayRawBufferRefs() = default;
200+
SharedArrayRawBufferRefs(SharedArrayRawBufferRefs&& other) = default;
201+
SharedArrayRawBufferRefs& operator=(SharedArrayRawBufferRefs&& other);
202+
~SharedArrayRawBufferRefs();
203+
204+
MOZ_MUST_USE bool acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf);
205+
MOZ_MUST_USE bool acquireAll(JSContext* cx, const SharedArrayRawBufferRefs& that);
206+
void takeOwnership(SharedArrayRawBufferRefs&&);
207+
void releaseAll();
208+
209+
private:
210+
js::Vector<js::SharedArrayRawBuffer*, 0, js::SystemAllocPolicy> refs_;
211+
};
212+
}
213+
191214
class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) :
192215
public mozilla::BufferList<js::SystemAllocPolicy>
193216
{
@@ -201,6 +224,7 @@ class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) :
201224
const JSStructuredCloneCallbacks* callbacks_;
202225
void* closure_;
203226
OwnTransferablePolicy ownTransferables_;
227+
js::SharedArrayRawBufferRefs refsHeld_;
204228

205229
void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks,
206230
void* closure,
@@ -279,7 +303,8 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
279303
void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
280304

281305
/** Copy some memory. It will be automatically freed by the destructor. */
282-
bool copy(const JSStructuredCloneData& data, uint32_t version=JS_STRUCTURED_CLONE_VERSION,
306+
bool copy(JSContext* cx, const JSStructuredCloneData& data,
307+
uint32_t version=JS_STRUCTURED_CLONE_VERSION,
283308
const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr);
284309

285310
/**

js/src/vm/StructuredClone.cpp

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,69 @@ struct BufferIterator {
229229
typename BufferList::IterImpl mIter;
230230
};
231231

232+
SharedArrayRawBufferRefs&
233+
SharedArrayRawBufferRefs::operator=(SharedArrayRawBufferRefs&& other)
234+
{
235+
takeOwnership(Move(other));
236+
return *this;
237+
}
238+
239+
SharedArrayRawBufferRefs::~SharedArrayRawBufferRefs()
240+
{
241+
releaseAll();
242+
}
243+
244+
bool
245+
SharedArrayRawBufferRefs::acquire(JSContext* cx, SharedArrayRawBuffer* rawbuf)
246+
{
247+
if (!refs_.append(rawbuf)) {
248+
ReportOutOfMemory(cx);
249+
return false;
250+
}
251+
252+
if (!rawbuf->addReference()) {
253+
refs_.popBack();
254+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
255+
return false;
256+
}
257+
258+
return true;
259+
}
260+
261+
bool
262+
SharedArrayRawBufferRefs::acquireAll(JSContext* cx, const SharedArrayRawBufferRefs& that)
263+
{
264+
if (!refs_.reserve(refs_.length() + that.refs_.length())) {
265+
ReportOutOfMemory(cx);
266+
return false;
267+
}
268+
269+
for (auto ref : that.refs_) {
270+
if (!ref->addReference()) {
271+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
272+
return false;
273+
}
274+
MOZ_ALWAYS_TRUE(refs_.append(ref));
275+
}
276+
277+
return true;
278+
}
279+
280+
void
281+
SharedArrayRawBufferRefs::takeOwnership(SharedArrayRawBufferRefs&& other)
282+
{
283+
MOZ_ASSERT(refs_.empty());
284+
refs_ = Move(other.refs_);
285+
}
286+
287+
void
288+
SharedArrayRawBufferRefs::releaseAll()
289+
{
290+
for (auto ref : refs_)
291+
ref->dropReference();
292+
refs_.clear();
293+
}
294+
232295
struct SCOutput {
233296
public:
234297
using Iter = BufferIterator<uint64_t, TempAllocPolicy>;
@@ -408,6 +471,9 @@ struct JSStructuredCloneWriter {
408471
bool extractBuffer(JSStructuredCloneData* data) {
409472
bool success = out.extractBuffer(data);
410473
if (success) {
474+
// Move the SharedArrayRawBuf references here, SCOutput::extractBuffer
475+
// moves the serialized data.
476+
data->refsHeld_.takeOwnership(Move(refsHeld));
411477
data->setOptionalCallbacks(callbacks, closure,
412478
OwnTransferablePolicy::OwnsTransferablesIfAny);
413479
}
@@ -486,6 +552,9 @@ struct JSStructuredCloneWriter {
486552

487553
const JS::CloneDataPolicy cloneDataPolicy;
488554

555+
// SharedArrayRawBuffers whose reference counts we have incremented.
556+
SharedArrayRawBufferRefs refsHeld;
557+
489558
friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
490559
friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
491560
friend bool JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj);
@@ -1161,12 +1230,8 @@ JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
11611230
Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
11621231
SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
11631232

1164-
// Avoids a race condition where the parent thread frees the buffer
1165-
// before the child has accepted the transferable.
1166-
if (!rawbuf->addReference()) {
1167-
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
1233+
if (!refsHeld.acquire(context(), rawbuf))
11681234
return false;
1169-
}
11701235

11711236
intptr_t p = reinterpret_cast<intptr_t>(rawbuf);
11721237
return out.writePair(SCTAG_SHARED_ARRAY_BUFFER_OBJECT, static_cast<uint32_t>(sizeof(p))) &&
@@ -1894,18 +1959,24 @@ JSStructuredCloneReader::readSharedArrayBuffer(uint32_t nbytes, MutableHandleVal
18941959
// in any case. Just fail at the receiving end if we can't handle it.
18951960

18961961
if (!context()->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled()) {
1897-
// The sending side performed a reference increment before sending.
1898-
// Account for that here before leaving.
1899-
if (rawbuf)
1900-
rawbuf->dropReference();
1901-
19021962
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_DISABLED);
19031963
return false;
19041964
}
19051965

1906-
// The constructor absorbs the reference count increment performed by the sender.
1966+
// The new object will have a new reference to the rawbuf.
1967+
1968+
if (!rawbuf->addReference()) {
1969+
JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_SAB_REFCNT_OFLO);
1970+
return false;
1971+
}
1972+
19071973
JSObject* obj = SharedArrayBufferObject::New(context(), rawbuf);
19081974

1975+
if (!obj) {
1976+
rawbuf->dropReference();
1977+
return false;
1978+
}
1979+
19091980
vp.setObject(*obj);
19101981
return true;
19111982
}
@@ -2591,13 +2662,14 @@ JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCal
25912662
if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny)
25922663
DiscardTransferables(data_, callbacks, closure);
25932664
data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables;
2665+
data_.refsHeld_.releaseAll();
25942666
data_.Clear();
25952667
version_ = 0;
25962668
}
25972669

25982670
bool
2599-
JSAutoStructuredCloneBuffer::copy(const JSStructuredCloneData& srcData, uint32_t version,
2600-
const JSStructuredCloneCallbacks* callbacks,
2671+
JSAutoStructuredCloneBuffer::copy(JSContext* cx, const JSStructuredCloneData& srcData,
2672+
uint32_t version, const JSStructuredCloneCallbacks* callbacks,
26012673
void* closure)
26022674
{
26032675
// transferable objects cannot be copied
@@ -2608,11 +2680,16 @@ JSAutoStructuredCloneBuffer::copy(const JSStructuredCloneData& srcData, uint32_t
26082680

26092681
auto iter = srcData.Iter();
26102682
while (!iter.Done()) {
2611-
data_.WriteBytes(iter.Data(), iter.RemainingInSegment());
2612-
iter.Advance(srcData, iter.RemainingInSegment());
2683+
if (!data_.WriteBytes(iter.Data(), iter.RemainingInSegment()))
2684+
return false;
2685+
iter.Advance(srcData, iter.RemainingInSegment());
26132686
}
26142687

26152688
version_ = version;
2689+
2690+
if (!data_.refsHeld_.acquireAll(cx, srcData.refsHeld_))
2691+
return false;
2692+
26162693
data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables);
26172694
return true;
26182695
}

0 commit comments

Comments
 (0)