Skip to content

Commit 5df2408

Browse files
author
Jianchun Xu
committed
[MERGE chakra-core#1291 @jianchun] 1607 servicing fixes
Merge pull request chakra-core#1291 from jianchun:pr1.2 This change combined fixes for CVE-2016-3259, CVE-2016-3260, CVE-2016-3265, CVE-2016-3269, CVE-2016-3271 and MS16-085. MSFT:7558512: [MSRC 33480] Mitigation Bypass Submission - InterpreterThunkEmitter Bypass CFG Issue. InterpreterStackFrame class has a member called interpreterThunk which stores the address of our interpreter function's address (regular or the asmjs one). The hacker took advantage of this address being stored in the heap memory and corrupted the same to reference a vulnerable shell code. We do not emit a CFG check for this address before calling, because this is a direct call and not an indirect call. Fix. This field is replaced with a boolean - to decide between regular/asmjs interpreter thunk. The address of the interpreter function is obtained in the function which is emitting the code, directly. This code has been present since the beginning - But this has to serviced only for chakra.dll (till th1), as we don't have CFG support before that. Tests. MSFT:7424216: [MSRC 33319] Chakra Type Confusion JavascriptArray::InternalCopyNativeFloatArrayElements - Individual [MSRC] Type confusion bug in ChakraCore JavascriptArray::InternalCopyNativeFloatArrayElements. MSFT:7527933: [MSRC 33383] Chakra JavascriptArray::ForEachOwnMissingArrayIndexOfObject - Individual [MSRC] Uninitialized stack variable in ChakraCore JavascriptArray::ForEachOwnMissingArrayIndexOfObject Component. Fix by ensuring stack variable was assigned before using. MSFT:7572196: [MSRC 33354] Edge Chakra ArrayBuffer.transfer - Zero Day Initiative Fix malloc/realloc usage in ES6 experimental feature ArrayBuffer.transfer. Should zero extra memory in either malloc or realloc case. MSFT:7387125 7387131 7387136 7387145 7387150 7424221 7424227: [MSRC 33299] Chakra Type Confusion in JavascriptArray::EntryFrom - Individual [MSRC] Type Confusion in Array built-ins DirectSetItemAt() is used in numerous Array built-ins without type-checking, where new objects may be created through a user-defined constructor. Fix by adding type-checking for type-specialized helper functions, and replacing DirectSetItemAt() calls with calls to virtual SetItem() functions where applicable. MSFT:7424474: [MSRC 33332] Edge ReadAV in chakra!Js::JavascriptOperators::StrictEqual+0x18 - Individual During sort prep we set orig[i] to missing_item. If an exception occurs in the middle (e.g. in "toString"), orig[i] will remain value missing_item, the Array's has_missing_item state could be wrong (wasn't updated), and the Array's content is also corrupted. Fixed by removing setting orig[i] to missing_item in prep. Do that after sort completion. (It is required to maintain segment length...end to contain only missing_item value.)
2 parents eba8a2f + 17f3d4a commit 5df2408

11 files changed

Lines changed: 390 additions & 131 deletions

lib/Backend/InterpreterThunkEmitter.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,13 @@ const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk);
189189
const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call);
190190
const uint InterpreterThunkEmitter::ThunksPerBlock = (BlockSize - HeaderSize) / ThunkSize;
191191

192-
InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, void * interpreterThunk) :
192+
InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
193193
emitBufferManager(allocator, codePageAllocators, /*scriptContext*/ nullptr, _u("Interpreter thunk buffer")),
194194
allocation(nullptr),
195195
allocator(allocator),
196196
thunkCount(0),
197197
thunkBuffer(nullptr),
198-
interpreterThunk(interpreterThunk)
198+
isAsmInterpreterThunk(isAsmInterpreterThunk)
199199
{
200200
}
201201

@@ -253,6 +253,20 @@ void InterpreterThunkEmitter::NewThunkBlock()
253253
DWORD bufferSize = BlockSize;
254254
DWORD thunkCount = 0;
255255

256+
void * interpreterThunk = nullptr;
257+
258+
// the static interpreter thunk invoked by the dynamic emitted thunk
259+
#ifdef ASMJS_PLAT
260+
if (isAsmInterpreterThunk)
261+
{
262+
interpreterThunk = Js::InterpreterStackFrame::InterpreterAsmThunk;
263+
}
264+
else
265+
#endif
266+
{
267+
interpreterThunk = Js::InterpreterStackFrame::InterpreterThunk;
268+
}
269+
256270
allocation = emitBufferManager.AllocateBuffer(bufferSize, &buffer);
257271
if (!emitBufferManager.ProtectBufferWithExecuteReadWriteForInterpreter(allocation))
258272
{
@@ -280,7 +294,7 @@ void InterpreterThunkEmitter::NewThunkBlock()
280294

281295
// Copy the thunk buffer and modify it.
282296
js_memcpy_s(currentBuffer, bytesRemaining, InterpreterThunk, HeaderSize);
283-
EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize);
297+
EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize, interpreterThunk);
284298
currentBuffer += HeaderSize;
285299
bytesRemaining -= HeaderSize;
286300

@@ -359,16 +373,16 @@ void InterpreterThunkEmitter::NewThunkBlock()
359373

360374

361375
#if _M_ARM
362-
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
376+
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
363377
{
364378
_Analysis_assume_(thunkSize == HeaderSize);
365379
// Encode MOVW
366-
DWORD lowerThunkBits = (uint32)this->interpreterThunk & 0x0000FFFF;
380+
DWORD lowerThunkBits = (uint32)interpreterThunk & 0x0000FFFF;
367381
DWORD movW = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/1, lowerThunkBits);
368382
Emit(thunkBuffer,ThunkAddressOffset, movW);
369383

370384
// Encode MOVT
371-
DWORD higherThunkBits = ((uint32)this->interpreterThunk & 0xFFFF0000) >> 16;
385+
DWORD higherThunkBits = ((uint32)interpreterThunk & 0xFFFF0000) >> 16;
372386
DWORD movT = EncodeMove(/*Opcode*/ 0x0000F2C0, /*register*/1, higherThunkBits);
373387
Emit(thunkBuffer, ThunkAddressOffset + sizeof(movW), movT);
374388

@@ -424,7 +438,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
424438
}
425439

426440
#elif _M_ARM64
427-
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
441+
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
428442
{
429443
int addrOffset = ThunkAddressOffset;
430444

@@ -434,28 +448,28 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE
434448
// Following 4 MOV Instrs are to move the 64-bit address of the InterpreterThunk address into register x1.
435449

436450
// Encode MOVZ (movz x1, #<interpreterThunk 16-0 bits>)
437-
DWORD lowerThunkBits = (uint64)this->interpreterThunk & 0x0000FFFF;
451+
DWORD lowerThunkBits = (uint64)interpreterThunk & 0x0000FFFF;
438452
DWORD movZ = EncodeMove(/*Opcode*/ 0xD2800000, /*register x1*/1, lowerThunkBits); // no shift; hw = 00
439453
Emit(thunkBuffer,addrOffset, movZ);
440454
AssertMsg(sizeof(movZ) == 4, "movZ has to be 32-bit encoded");
441455
addrOffset+= sizeof(movZ);
442456

443457
// Encode MOVK (movk x1, #<interpreterThunk 32-16 bits>, lsl #16)
444-
DWORD higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF0000) >> 16;
458+
DWORD higherThunkBits = ((uint64)interpreterThunk & 0xFFFF0000) >> 16;
445459
DWORD movK = EncodeMove(/*Opcode*/ 0xF2A00000, /*register x1*/1, higherThunkBits); // left shift 16 bits; hw = 01
446460
Emit(thunkBuffer, addrOffset, movK);
447461
AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
448462
addrOffset+= sizeof(movK);
449463

450464
// Encode MOVK (movk x1, #<interpreterThunk 48-32 bits>, lsl #16)
451-
higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF00000000) >> 32;
465+
higherThunkBits = ((uint64)interpreterThunk & 0xFFFF00000000) >> 32;
452466
movK = EncodeMove(/*Opcode*/ 0xF2C00000, /*register x1*/1, higherThunkBits); // left shift 32 bits; hw = 02
453467
Emit(thunkBuffer, addrOffset, movK);
454468
AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
455469
addrOffset += sizeof(movK);
456470

457471
// Encode MOVK (movk x1, #<interpreterThunk 64-48 bits>, lsl #16)
458-
higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF000000000000) >> 48;
472+
higherThunkBits = ((uint64)interpreterThunk & 0xFFFF000000000000) >> 48;
459473
movK = EncodeMove(/*Opcode*/ 0xF2E00000, /*register x1*/1, higherThunkBits); // left shift 48 bits; hw = 03
460474
AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
461475
Emit(thunkBuffer, addrOffset, movK);
@@ -498,7 +512,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
498512
function->FrameSize = 5; // the number of bytes of stack that is allocated for this function divided by 16
499513
}
500514
#else
501-
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
515+
void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
502516
{
503517
_Analysis_assume_(thunkSize == HeaderSize);
504518
Emit(thunkBuffer, ThunkAddressOffset, (uintptr_t)interpreterThunk);

lib/Backend/InterpreterThunkEmitter.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class InterpreterThunkEmitter
6161
EmitBufferAllocation *allocation;
6262
SListBase<ThunkBlock> thunkBlocks;
6363
SListBase<ThunkBlock> freeListedThunkBlocks;
64-
void * interpreterThunk; // the static interpreter thunk invoked by the dynamic emitted thunk
64+
bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
6565
BYTE* thunkBuffer;
6666
ArenaAllocator* allocator;
6767
DWORD thunkCount; // Count of thunks available in the current thunk block
@@ -94,7 +94,7 @@ class InterpreterThunkEmitter
9494

9595
/* ------private helpers -----------*/
9696
void NewThunkBlock();
97-
void EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize);
97+
void EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk);
9898
#if defined(_M_ARM32_OR_ARM64)
9999
DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
100100
void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
@@ -116,7 +116,7 @@ class InterpreterThunkEmitter
116116
static const uint BlockSize;
117117
static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
118118

119-
InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, void * interpreterThunk);
119+
InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk = false);
120120

121121
BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
122122

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,13 +1155,12 @@ if (!sourceList)
11551155
}
11561156

11571157
#if DYNAMIC_INTERPRETER_THUNK
1158-
interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
1159-
Js::InterpreterStackFrame::InterpreterThunk);
1158+
interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators());
11601159
#endif
11611160

11621161
#ifdef ASMJS_PLAT
11631162
asmJsInterpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
1164-
Js::InterpreterStackFrame::InterpreterAsmThunk);
1163+
true);
11651164
#endif
11661165

11671166
JS_ETW(EtwTrace::LogScriptContextLoadEvent(this));

lib/Runtime/Library/ArrayBuffer.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,29 @@ namespace Js
878878
#endif
879879
}
880880

881+
// Copy memory from src to dst, truncate if dst smaller, zero extra memory
882+
// if dst larger
883+
static void MemCpyZero(__bcount(dstSize) BYTE* dst, size_t dstSize,
884+
__in_bcount(count) const BYTE* src, size_t count)
885+
{
886+
js_memcpy_s(dst, dstSize, src, min(dstSize, count));
887+
if (dstSize > count)
888+
{
889+
ZeroMemory(dst + count, dstSize - count);
890+
}
891+
}
892+
893+
// Same as realloc but zero newly allocated portion if newSize > oldSize
894+
static BYTE* ReallocZero(BYTE* ptr, size_t oldSize, size_t newSize)
895+
{
896+
BYTE* ptrNew = (BYTE*)realloc(ptr, newSize);
897+
if (ptrNew && newSize > oldSize)
898+
{
899+
ZeroMemory(ptrNew + oldSize, newSize - oldSize);
900+
}
901+
return ptrNew;
902+
}
903+
881904
ArrayBuffer * JavascriptArrayBuffer::TransferInternal(uint32 newBufferLength)
882905
{
883906
ArrayBuffer* newArrayBuffer;
@@ -940,7 +963,7 @@ namespace Js
940963
recycler->ReportExternalMemoryFailure(newBufferLength);
941964
JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
942965
}
943-
js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
966+
MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
944967
}
945968
}
946969
else
@@ -949,12 +972,12 @@ namespace Js
949972
{
950973
// we are transferring from an unoptimized buffer, but new length can be optimized, so move to that
951974
newBuffer = (BYTE*)JavascriptArrayBuffer::AllocWrapper(newBufferLength);
952-
js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
975+
MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
953976
}
954977
else if (newBufferLength != this->bufferLength)
955978
{
956979
// both sides will just be regular ArrayBuffer, so realloc
957-
newBuffer = (BYTE*)realloc(this->buffer, newBufferLength);
980+
newBuffer = ReallocZero(this->buffer, this->bufferLength, newBufferLength);
958981
if (!newBuffer)
959982
{
960983
recycler->ReportExternalMemoryFailure(newBufferLength);

0 commit comments

Comments
 (0)