Skip to content

Commit 15df2a6

Browse files
committed
[CVE-2019-0640] Bug report for Edge/Chakra: Missing marshalling for Promise result
I also added mitigations for bad things that can happen when calling into a closed script context. 1. We delete xdata before unregistering it, which can lead to UAF if we call address of a closed function. Windows Exception code unconditionally jumps to handler address (i.e. without CFG check), so this can bypass CFG. I changed to delete after unregistering. 2. We zero code pages when we close script context, which could be exploitable on x86. I changed to fill with debugbreak.
1 parent 5f6dea1 commit 15df2a6

9 files changed

+72
-45
lines changed

lib/Backend/CodeGenWorkItem.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -210,19 +210,7 @@ void CodeGenWorkItem::OnWorkItemProcessFail(NativeCodeGenerator* codeGen)
210210
#if PDATA_ENABLED & defined(_WIN32)
211211
if (this->entryPointInfo)
212212
{
213-
XDataAllocation * xdataAllocation = this->entryPointInfo->GetNativeEntryPointData()->GetXDataInfo();
214-
if (xdataAllocation)
215-
{
216-
void* functionTable = xdataAllocation->functionTable;
217-
if (functionTable)
218-
{
219-
if (!DelayDeletingFunctionTable::AddEntry(functionTable))
220-
{
221-
PHASE_PRINT_TESTTRACE1(Js::XDataPhase, _u("[%d]OnWorkItemProcessFail: Failed to add to slist, table: %llx\n"), GetCurrentThreadId(), functionTable);
222-
DelayDeletingFunctionTable::DeleteFunctionTable(functionTable);
223-
}
224-
}
225-
}
213+
this->entryPointInfo->GetNativeEntryPointData()->CleanupXDataInfo();
226214
}
227215
#endif
228216
codeGen->FreeNativeCodeGenAllocation(this->allocation->allocation->address);

lib/Backend/NativeEntryPointData.cpp

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -321,20 +321,14 @@ NativeEntryPointData::CleanupXDataInfo()
321321
{
322322
if (this->xdataInfo != nullptr)
323323
{
324+
XDataAllocator::Unregister(this->xdataInfo);
324325
#ifdef _WIN32
325326
if (this->xdataInfo->functionTable
326-
&& !DelayDeletingFunctionTable::AddEntry(this->xdataInfo->functionTable))
327+
&& !DelayDeletingFunctionTable::AddEntry(this->xdataInfo))
327328
{
328-
DelayDeletingFunctionTable::DeleteFunctionTable(this->xdataInfo->functionTable);
329+
DelayDeletingFunctionTable::DeleteFunctionTable(this->xdataInfo);
329330
}
330331
#endif
331-
XDataAllocator::Unregister(this->xdataInfo);
332-
#if defined(_M_ARM)
333-
if (JITManager::GetJITManager()->IsOOPJITEnabled())
334-
#endif
335-
{
336-
HeapDelete(this->xdataInfo);
337-
}
338332
this->xdataInfo = nullptr;
339333
}
340334
}

lib/Backend/NativeEntryPointData.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,15 @@ class NativeEntryPointData
7171

7272
#if PDATA_ENABLED
7373
XDataAllocation* GetXDataInfo() { return this->xdataInfo; }
74-
void SetXDataInfo(XDataAllocation* xdataInfo) { this->xdataInfo = xdataInfo; }
74+
void CleanupXDataInfo();
75+
void SetXDataInfo(XDataAllocation* xdataInfo) { this->xdataInfo = xdataInfo; }
7576
#endif
7677
private:
7778
void RegisterEquivalentTypeCaches(Js::ScriptContext * scriptContext, Js::EntryPointInfo * entryPointInfo);
7879
void UnregisterEquivalentTypeCaches(Js::ScriptContext * scriptContext);
7980
void FreePropertyGuards();
8081

8182
void FreeNativeCode(Js::ScriptContext * scriptContext, bool isShutdown);
82-
#if PDATA_ENABLED
83-
void CleanupXDataInfo();
84-
#endif
8583

8684
FieldNoBarrier(Js::JavascriptMethod) nativeAddress;
8785
FieldNoBarrier(Js::JavascriptMethod) thunkAddress;

lib/Backend/PDataManager.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ void PDataManager::UnregisterPdata(RUNTIME_FUNCTION* pdata)
5151
{
5252
if (AutoSystemInfo::Data.IsWin8OrLater())
5353
{
54-
// TODO: need to move to background?
55-
DelayDeletingFunctionTable::DeleteFunctionTable(pdata);
54+
NtdllLibrary::Instance->DeleteGrowableFunctionTable(pdata);
5655
}
5756
else
5857
{

lib/Common/Memory/Chakra.Common.Memory.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
<ClCompile>
2727
<AdditionalIncludeDirectories>
2828
$(MSBuildThisFileDirectory);
29+
$(MSBuildThisFileDirectory)..\..\JITClient;
30+
$(ChakraJITIDLIntDir);
2931
$(MSBuildThisFileDirectory)..;
3032
%(AdditionalIncludeDirectories)
3133
</AdditionalIncludeDirectories>

lib/Common/Memory/DelayDeletingFunctionTable.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "Memory/XDataAllocator.h"
88
#if PDATA_ENABLED && defined(_WIN32)
99
#include "Core/DelayLoadLibrary.h"
10+
#include "JITClient.h"
1011
#include <malloc.h>
1112

1213
CriticalSection DelayDeletingFunctionTable::cs;
@@ -34,14 +35,14 @@ DelayDeletingFunctionTable::~DelayDeletingFunctionTable()
3435
}
3536

3637

37-
bool DelayDeletingFunctionTable::AddEntry(FunctionTableHandle ft)
38+
bool DelayDeletingFunctionTable::AddEntry(XDataAllocation* xdata)
3839
{
3940
if (Head)
4041
{
4142
FunctionTableNode* node = (FunctionTableNode*)_aligned_malloc(sizeof(FunctionTableNode), MEMORY_ALLOCATION_ALIGNMENT);
4243
if (node)
4344
{
44-
node->functionTable = ft;
45+
node->xdata = xdata;
4546
InterlockedPushEntrySList(Head, &(node->itemEntry));
4647
return true;
4748
}
@@ -61,7 +62,7 @@ void DelayDeletingFunctionTable::Clear()
6162
while (entry)
6263
{
6364
FunctionTableNode* list = (FunctionTableNode*)entry;
64-
DeleteFunctionTable(list->functionTable);
65+
DeleteFunctionTable(list->xdata);
6566
_aligned_free(entry);
6667
entry = InterlockedPopEntrySList(Head);
6768
}
@@ -73,9 +74,16 @@ bool DelayDeletingFunctionTable::IsEmpty()
7374
return QueryDepthSList(Head) == 0;
7475
}
7576

76-
void DelayDeletingFunctionTable::DeleteFunctionTable(void* functionTable)
77+
void DelayDeletingFunctionTable::DeleteFunctionTable(XDataAllocation* xdata)
7778
{
78-
NtdllLibrary::Instance->DeleteGrowableFunctionTable(functionTable);
79+
NtdllLibrary::Instance->DeleteGrowableFunctionTable(xdata->functionTable);
80+
81+
#if defined(_M_ARM)
82+
if (JITManager::GetJITManager()->IsOOPJITEnabled())
83+
#endif
84+
{
85+
HeapDelete(xdata);
86+
}
7987
}
8088

8189
#endif

lib/Common/Memory/SectionAllocWrapper.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66

77
#if _WIN32
88
#if ENABLE_OOP_NATIVE_CODEGEN
9-
#include "../Core/DelayLoadLibrary.h"
9+
#include "Core/DelayLoadLibrary.h"
10+
11+
#include "XDataAllocator.h"
12+
#include "CustomHeap.h"
1013

1114
#ifdef NTDDI_WIN10_RS2
1215
#if (NTDDI_VERSION >= NTDDI_WIN10_RS2)
@@ -603,6 +606,19 @@ SectionAllocWrapper::AllocPages(LPVOID requestAddress, size_t pageCount, DWORD a
603606
{
604607
return nullptr;
605608
}
609+
610+
// pages could be filled with debugbreak
611+
// zero one page at a time to minimize working set impact while zeroing
612+
for (size_t i = 0; i < dwSize / AutoSystemInfo::PageSize; ++i)
613+
{
614+
LPVOID localAddr = AllocLocal((char*)requestAddress + i * AutoSystemInfo::PageSize, AutoSystemInfo::PageSize);
615+
if (localAddr == nullptr)
616+
{
617+
return nullptr;
618+
}
619+
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
620+
FreeLocal(localAddr);
621+
}
606622
address = requestAddress;
607623
}
608624

@@ -677,10 +693,10 @@ BOOL SectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFreeType
677693
{
678694
return FALSE;
679695
}
680-
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
696+
697+
CustomHeap::FillDebugBreak((BYTE*)localAddr, AutoSystemInfo::PageSize);
681698
FreeLocal(localAddr);
682699
}
683-
UnlockMemory(this->process, lpAddress, dwSize);
684700
}
685701

686702
return TRUE;
@@ -928,6 +944,19 @@ LPVOID PreReservedSectionAllocWrapper::AllocPages(LPVOID lpAddress, DECLSPEC_GUA
928944

929945
addressToReserve = (char*)lpAddress;
930946
freeSegmentsBVIndex = (uint)((addressToReserve - (char*)this->preReservedStartAddress) / AutoSystemInfo::Data.GetAllocationGranularityPageSize());
947+
948+
// pages could be filled with debugbreak
949+
// zero one page at a time to minimize working set impact while zeroing
950+
for (size_t i = 0; i < dwSize / AutoSystemInfo::PageSize; ++i)
951+
{
952+
LPVOID localAddr = AllocLocal((char*)lpAddress + i * AutoSystemInfo::PageSize, AutoSystemInfo::PageSize);
953+
if (localAddr == nullptr)
954+
{
955+
return nullptr;
956+
}
957+
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
958+
FreeLocal(localAddr);
959+
}
931960
#if DBG
932961
uint numOfSegments = (uint)ceil((double)dwSize / (double)AutoSystemInfo::Data.GetAllocationGranularityPageSize());
933962
Assert(numOfSegments != 0);
@@ -978,12 +1007,17 @@ PreReservedSectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFr
9781007
{
9791008
return FALSE;
9801009
}
981-
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
1010+
if ((dwFreeType & MEM_RELEASE) == MEM_RELEASE)
1011+
{
1012+
ZeroMemory(localAddr, AutoSystemInfo::PageSize);
1013+
}
1014+
else
1015+
{
1016+
CustomHeap::FillDebugBreak((BYTE*)localAddr, AutoSystemInfo::PageSize);
1017+
}
9821018
FreeLocal(localAddr);
9831019
}
9841020

985-
UnlockMemory(this->process, lpAddress, dwSize);
986-
9871021
size_t requestedNumOfSegments = dwSize / AutoSystemInfo::Data.GetAllocationGranularityPageSize();
9881022
Assert(requestedNumOfSegments <= MAXUINT32);
9891023

@@ -1000,6 +1034,8 @@ PreReservedSectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFr
10001034
AssertMsg(freeSegmentsBVIndex < PreReservedAllocationSegmentCount, "Invalid Index ?");
10011035
freeSegments.SetRange(freeSegmentsBVIndex, static_cast<uint>(requestedNumOfSegments));
10021036
PreReservedHeapTrace(_u("MEM_RELEASE: Address: 0x%p of size: 0x%x * 0x%x bytes\n"), lpAddress, requestedNumOfSegments, AutoSystemInfo::Data.GetAllocationGranularityPageSize());
1037+
1038+
UnlockMemory(this->process, lpAddress, dwSize);
10031039
}
10041040

10051041
return TRUE;

lib/Common/Memory/XDataAllocator.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
struct FunctionTableNode
1717
{
1818
SLIST_ENTRY itemEntry;
19-
FunctionTableHandle functionTable;
19+
XDataAllocation* xdata;
2020
};
2121

2222
struct DelayDeletingFunctionTable
@@ -28,9 +28,9 @@ struct DelayDeletingFunctionTable
2828
DelayDeletingFunctionTable();
2929
~DelayDeletingFunctionTable();
3030

31-
static bool AddEntry(FunctionTableHandle ft);
31+
static bool AddEntry(XDataAllocation* xdata);
3232
static void Clear();
3333
static bool IsEmpty();
34-
static void DeleteFunctionTable(void* functionTable);
34+
static void DeleteFunctionTable(XDataAllocation* xdata);
3535
};
3636
#endif

lib/Runtime/Library/JavascriptPromise.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,15 +1144,17 @@ namespace Js
11441144
sourcePromise->reactions->Prepend(pair);
11451145
break;
11461146
case PromiseStatusCode_HasResolution:
1147-
EnqueuePromiseReactionTask(resolveReaction, sourcePromise->result, scriptContext);
1147+
EnqueuePromiseReactionTask(resolveReaction, CrossSite::MarshalVar(scriptContext, sourcePromise->result), scriptContext);
11481148
break;
11491149
case PromiseStatusCode_HasRejection:
1150+
{
11501151
if (!sourcePromise->GetIsHandled())
11511152
{
1152-
scriptContext->GetLibrary()->CallNativeHostPromiseRejectionTracker(sourcePromise, sourcePromise->result, true);
1153+
scriptContext->GetLibrary()->CallNativeHostPromiseRejectionTracker(sourcePromise, CrossSite::MarshalVar(scriptContext, sourcePromise->result), true);
11531154
}
1154-
EnqueuePromiseReactionTask(rejectReaction, sourcePromise->result, scriptContext);
1155+
EnqueuePromiseReactionTask(rejectReaction, CrossSite::MarshalVar(scriptContext, sourcePromise->result), scriptContext);
11551156
break;
1157+
}
11561158
default:
11571159
AssertMsg(false, "Promise status is in an invalid state");
11581160
break;

0 commit comments

Comments
 (0)