Skip to content

Commit 21bf50f

Browse files
committed
[1.9>1.10] [1.8>1.9] [MERGE #5417 @sethbrenith] JsRunSerialized crash
Merge pull request #5417 from sethbrenith:user/sethb/script-buffer-lifetime JsrtSourceHolder needs to ensure that the input ArrayBuffer's data stays alive until after all related FunctionEntryPointInfos have finalized (because they fetch their name for ETW logging during Finalize). Fixes OS:18095136
2 parents 8bdef9c + 9c6f4fb commit 21bf50f

11 files changed

+149
-16
lines changed

lib/Jsrt/ChakraCore.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,8 @@ CHAKRA_API
694694
/// Requires an active script context.
695695
/// </para>
696696
/// <para>
697-
/// The runtime will hold on to the buffer until all instances of any functions created from
698-
/// the buffer are garbage collected.
697+
/// The runtime will detach the data from the buffer and hold on to it until all
698+
/// instances of any functions created from the buffer are garbage collected.
699699
/// </para>
700700
/// </remarks>
701701
/// <param name="buffer">The serialized script as an ArrayBuffer (preferably ExternalArrayBuffer).</param>

lib/Jsrt/Jsrt.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,26 @@ CHAKRA_API JsPrivateCollectGarbageSkipStack(_In_ JsRuntimeHandle runtimeHandle)
470470
{
471471
return JsCollectGarbageCommon<CollectNowExhaustiveSkipStack>(runtimeHandle);
472472
}
473+
474+
CHAKRA_API JsPrivateDetachArrayBuffer(_In_ JsValueRef ref, _Out_ void** detachedState)
475+
{
476+
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode
477+
{
478+
VALIDATE_JSREF(ref);
479+
*detachedState = Js::JavascriptOperators::DetachVarAndGetState(ref);
480+
return JsNoError;
481+
});
482+
}
483+
484+
CHAKRA_API JsPrivateFreeDetachedArrayBuffer(_In_ void* detachedState)
485+
{
486+
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode
487+
{
488+
auto state = reinterpret_cast<Js::ArrayBufferDetachedStateBase*>(detachedState);
489+
state->CleanUp();
490+
return JsNoError;
491+
});
492+
}
473493
#endif
474494

475495
CHAKRA_API JsDisposeRuntime(_In_ JsRuntimeHandle runtimeHandle)
@@ -3855,7 +3875,7 @@ template <typename TLoadCallback, typename TUnloadCallback>
38553875
JsErrorCode RunSerializedScriptCore(
38563876
TLoadCallback scriptLoadCallback, TUnloadCallback scriptUnloadCallback,
38573877
JsSourceContext scriptLoadSourceContext, // only used by scriptLoadCallback
3858-
unsigned char *buffer, JsValueRef bufferVal,
3878+
unsigned char *buffer, Js::ArrayBuffer* bufferVal,
38593879
JsSourceContext sourceContext, const WCHAR *sourceUrl,
38603880
bool parseOnly, bool useParserStateCache, JsValueRef *result,
38613881
uint sourceIndex)
@@ -3910,7 +3930,7 @@ JsErrorCode RunSerializedScriptCore(
39103930
}
39113931

39123932
HRESULT hr;
3913-
3933+
39143934
Field(Js::FunctionBody*) functionBody = nullptr;
39153935

39163936
uint32 flags = 0;
@@ -5127,12 +5147,13 @@ CHAKRA_API JsParseSerialized(
51275147
return JsErrorInvalidArgument;
51285148
}
51295149

5130-
byte* buffer = Js::ArrayBuffer::FromVar(bufferVal)->GetBuffer();
5150+
Js::ArrayBuffer* arrayBuffer = Js::ArrayBuffer::FromVar(bufferVal);
5151+
byte* buffer = arrayBuffer->GetBuffer();
51315152

51325153
return RunSerializedScriptCore(
51335154
scriptLoadCallback, DummyScriptUnloadCallback,
51345155
sourceContext,// use the same user provided sourceContext as scriptLoadSourceContext
5135-
buffer, bufferVal, sourceContext, url, true, false, result, Js::Constants::InvalidSourceIndex);
5156+
buffer, arrayBuffer, sourceContext, url, true, false, result, Js::Constants::InvalidSourceIndex);
51365157
}
51375158

51385159
CHAKRA_API JsRunSerialized(
@@ -5160,12 +5181,13 @@ CHAKRA_API JsRunSerialized(
51605181
return JsErrorInvalidArgument;
51615182
}
51625183

5163-
byte* buffer = Js::ArrayBuffer::FromVar(bufferVal)->GetBuffer();
5184+
Js::ArrayBuffer* arrayBuffer = Js::ArrayBuffer::FromVar(bufferVal);
5185+
byte* buffer = arrayBuffer->GetBuffer();
51645186

51655187
return RunSerializedScriptCore(
51665188
scriptLoadCallback, DummyScriptUnloadCallback,
51675189
sourceContext, // use the same user provided sourceContext as scriptLoadSourceContext
5168-
buffer, bufferVal, sourceContext, url, false, false, result, Js::Constants::InvalidSourceIndex);
5190+
buffer, arrayBuffer, sourceContext, url, false, false, result, Js::Constants::InvalidSourceIndex);
51695191
}
51705192

51715193
CHAKRA_API JsCreatePromise(_Out_ JsValueRef *promise, _Out_ JsValueRef *resolve, _Out_ JsValueRef *reject)
@@ -5744,13 +5766,14 @@ CHAKRA_API JsRunScriptWithParserState(
57445766
return JsErrorInvalidArgument;
57455767
}
57465768

5747-
byte* buffer = Js::ArrayBuffer::FromVar(parserState)->GetBuffer();
5769+
Js::ArrayBuffer* arrayBuffer = Js::ArrayBuffer::FromVar(parserState);
5770+
byte* buffer = arrayBuffer->GetBuffer();
57485771
JsSerializedLoadScriptCallback dummy = DummyScriptLoadSourceCallbackForRunScriptWithParserState;
57495772

57505773
return RunSerializedScriptCore(
57515774
dummy, DummyScriptUnloadCallback,
57525775
sourceContext, // use the same user provided sourceContext as scriptLoadSourceContext
5753-
buffer, parserState, sourceContext, url, false, true, result, sourceIndex);
5776+
buffer, arrayBuffer, sourceContext, url, false, true, result, sourceIndex);
57545777
}
57555778

57565779
#endif // _CHAKRACOREBUILD

lib/Jsrt/JsrtExternalArrayBuffer.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,41 @@ namespace Js
1919
}
2020

2121
void JsrtExternalArrayBuffer::Finalize(bool isShutdown)
22+
{
23+
if (finalizeCallback != nullptr && !isDetached)
24+
{
25+
finalizeCallback(callbackState);
26+
}
27+
}
28+
29+
ArrayBufferDetachedStateBase* JsrtExternalArrayBuffer::CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
30+
{
31+
return HeapNew(JsrtExternalArrayBufferDetachedState, buffer, bufferLength, finalizeCallback, callbackState);
32+
};
33+
34+
JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::JsrtExternalArrayBufferDetachedState(
35+
BYTE* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState)
36+
: ExternalArrayBufferDetachedState(buffer, bufferLength), finalizeCallback(finalizeCallback), callbackState(callbackState)
37+
{}
38+
39+
void JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::ClearSelfOnly()
40+
{
41+
HeapDelete(this);
42+
}
43+
44+
void JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::DiscardState()
2245
{
2346
if (finalizeCallback != nullptr)
2447
{
2548
finalizeCallback(callbackState);
2649
}
50+
finalizeCallback = nullptr;
51+
}
52+
53+
ArrayBuffer* JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::Create(JavascriptLibrary* library)
54+
{
55+
ArrayBuffer* arr = JsrtExternalArrayBuffer::New(buffer, bufferLength, finalizeCallback, callbackState, library->GetArrayBufferType());
56+
JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(arr));
57+
return arr;
2758
}
2859
}

lib/Jsrt/JsrtExternalArrayBuffer.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Js {
1212
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JsrtExternalArrayBuffer);
1313

1414
JsrtExternalArrayBuffer(byte *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
15+
virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
1516

1617
public:
1718
static JsrtExternalArrayBuffer* New(byte *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
@@ -20,6 +21,17 @@ namespace Js {
2021
private:
2122
FieldNoBarrier(JsFinalizeCallback) finalizeCallback;
2223
Field(void *) callbackState;
24+
25+
class JsrtExternalArrayBufferDetachedState : public ExternalArrayBufferDetachedState
26+
{
27+
FieldNoBarrier(JsFinalizeCallback) finalizeCallback;
28+
Field(void *) callbackState;
29+
public:
30+
JsrtExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState);
31+
virtual void ClearSelfOnly() override;
32+
virtual void DiscardState() override;
33+
virtual ArrayBuffer* Create(JavascriptLibrary* library) override;
34+
};
2335
};
2436
}
2537
AUTO_REGISTER_RECYCLER_OBJECT_DUMPER(Js::JsrtExternalArrayBuffer, &Js::RecyclableObject::DumpObjectFunction);

lib/Jsrt/JsrtSourceHolder.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,6 @@ namespace Js
299299
this->mappedSource = nullptr;
300300
}
301301
this->mappedScriptValue = nullptr;
302-
this->mappedSerializedScriptValue = nullptr;
303302

304303
// Don't allow load or unload again after told to unload.
305304
scriptLoadCallback = nullptr;

lib/Jsrt/JsrtSourceHolder.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Js
1919

2020
#ifndef NTBUILD
2121
Field(JsValueRef) mappedScriptValue;
22-
Field(JsValueRef) mappedSerializedScriptValue;
22+
Field(DetachedStateBase*) mappedSerializedScriptValue;
2323
#endif
2424
Field(utf8char_t const *) mappedSource;
2525
Field(size_t) mappedSourceByteLength;
@@ -46,13 +46,13 @@ namespace Js
4646
JsrtSourceHolder(_In_ TLoadCallback scriptLoadCallback,
4747
_In_ TUnloadCallback scriptUnloadCallback,
4848
_In_ JsSourceContext sourceContext,
49-
JsValueRef serializedScriptValue = nullptr) :
49+
Js::ArrayBuffer* serializedScriptValue = nullptr) :
5050
scriptLoadCallback(scriptLoadCallback),
5151
scriptUnloadCallback(scriptUnloadCallback),
5252
sourceContext(sourceContext),
5353
#ifndef NTBUILD
5454
mappedScriptValue(nullptr),
55-
mappedSerializedScriptValue(serializedScriptValue),
55+
mappedSerializedScriptValue(serializedScriptValue == nullptr ? nullptr : serializedScriptValue->DetachAndGetState()),
5656
#endif
5757
mappedSourceByteLength(0),
5858
mappedSource(nullptr)
@@ -89,6 +89,15 @@ namespace Js
8989

9090
virtual void Dispose(bool isShutdown) override
9191
{
92+
#ifndef NTBUILD
93+
if (this->mappedSerializedScriptValue != nullptr)
94+
{
95+
// We have to extend the buffer data's lifetime until Dispose because
96+
// it might be used during finalization of other objects, such as
97+
// FunctionEntryPointInfo which wants to log its name.
98+
this->mappedSerializedScriptValue->CleanUp();
99+
}
100+
#endif
92101
}
93102

94103
virtual void Mark(Recycler * recycler) override

lib/Runtime/DetachedStateBase.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ namespace Js
4747
{
4848
Heap = 0x0,
4949
CoTask = 0x1,
50-
MemAlloc = 0x02
50+
MemAlloc = 0x02,
51+
External = 0x03,
5152
} ArrayBufferAllocationType;
5253

5354
class ArrayBufferDetachedStateBase : public DetachedStateBase

lib/Runtime/Library/ArrayBuffer.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ namespace Js
3737
case ArrayBufferAllocationType::MemAlloc:
3838
toReturn = library->CreateArrayBuffer(arrayBufferState->buffer, arrayBufferState->bufferLength);
3939
break;
40+
case ArrayBufferAllocationType::External:
41+
toReturn = static_cast<ExternalArrayBufferDetachedState*>(state)->Create(library);
42+
break;
4043
default:
4144
AssertMsg(false, "Unknown allocationType of ArrayBufferDetachedStateBase ");
4245
}
@@ -1066,11 +1069,27 @@ namespace Js
10661069
/* See ProjectionArrayBuffer::Finalize */
10671070
}
10681071

1072+
ArrayBuffer* ExternalArrayBufferDetachedState::Create(JavascriptLibrary* library)
1073+
{
1074+
return library->CreateExternalArrayBuffer(buffer, bufferLength);
1075+
}
1076+
10691077
ExternalArrayBuffer::ExternalArrayBuffer(byte *buffer, uint32 length, DynamicType *type)
10701078
: ArrayBuffer(buffer, length, type, true)
10711079
{
10721080
}
10731081

1082+
ExternalArrayBuffer* ExternalArrayBuffer::Create(byte* buffer, uint32 length, DynamicType * type)
1083+
{
1084+
// This type does not own the external memory, so don't AddExternalMemoryUsage like other ArrayBuffer types do
1085+
return RecyclerNewFinalized(type->GetScriptContext()->GetRecycler(), ExternalArrayBuffer, buffer, length, type);
1086+
}
1087+
1088+
ArrayBufferDetachedStateBase* ExternalArrayBuffer::CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
1089+
{
1090+
return HeapNew(ExternalArrayBufferDetachedState, buffer, bufferLength);
1091+
};
1092+
10741093
#if ENABLE_TTD
10751094
TTD::NSSnapObjects::SnapObjectType ExternalArrayBuffer::GetSnapTag_TTD() const
10761095
{
@@ -1096,4 +1115,23 @@ namespace Js
10961115
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapArrayBufferInfo*, TTD::NSSnapObjects::SnapObjectType::SnapArrayBufferObject>(objData, sabi);
10971116
}
10981117
#endif
1118+
1119+
ExternalArrayBufferDetachedState::ExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength)
1120+
: ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, ArrayBufferAllocationType::External)
1121+
{}
1122+
1123+
void ExternalArrayBufferDetachedState::ClearSelfOnly()
1124+
{
1125+
HeapDelete(this);
1126+
}
1127+
1128+
void ExternalArrayBufferDetachedState::DiscardState()
1129+
{
1130+
// Nothing to do as buffer is external
1131+
}
1132+
1133+
void ExternalArrayBufferDetachedState::Discard()
1134+
{
1135+
ClearSelfOnly();
1136+
}
10991137
}

lib/Runtime/Library/ArrayBuffer.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,15 +337,27 @@ namespace Js
337337
protected:
338338
DEFINE_VTABLE_CTOR(ExternalArrayBuffer, ArrayBuffer);
339339
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ExternalArrayBuffer);
340+
340341
public:
341342
ExternalArrayBuffer(byte *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
343+
static ExternalArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
342344
protected:
343-
virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override { Assert(UNREACHED); Throw::InternalError(); };
345+
virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
344346

345347
#if ENABLE_TTD
346348
public:
347349
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
348350
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
349351
#endif
350352
};
353+
354+
class ExternalArrayBufferDetachedState : public ArrayBufferDetachedStateBase
355+
{
356+
public:
357+
ExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength);
358+
virtual void ClearSelfOnly() override;
359+
virtual void DiscardState() override;
360+
virtual void Discard() override;
361+
virtual ArrayBuffer* Create(JavascriptLibrary* library);
362+
};
351363
}

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5750,6 +5750,13 @@ namespace Js
57505750
return arr;
57515751
}
57525752

5753+
ArrayBuffer* JavascriptLibrary::CreateExternalArrayBuffer(byte* buffer, uint32 length)
5754+
{
5755+
ArrayBuffer* arr = ExternalArrayBuffer::Create(buffer, length, arrayBufferType);
5756+
JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(arr));
5757+
return arr;
5758+
}
5759+
57535760
DataView* JavascriptLibrary::CreateDataView(ArrayBufferBase* arrayBuffer, uint32 offset, uint32 length)
57545761
{
57555762
DataView* dataView = RecyclerNew(this->GetRecycler(), DataView, arrayBuffer, offset, length, dataViewType);

0 commit comments

Comments
 (0)