Skip to content

Commit

Permalink
1607 servicing fixes
Browse files Browse the repository at this point in the history
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.)
  • Loading branch information
Jianchun Xu committed Jul 19, 2016
1 parent eba8a2f commit 17f3d4a
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 131 deletions.
38 changes: 26 additions & 12 deletions lib/Backend/InterpreterThunkEmitter.cpp
Expand Up @@ -189,13 +189,13 @@ const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk);
const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call);
const uint InterpreterThunkEmitter::ThunksPerBlock = (BlockSize - HeaderSize) / ThunkSize;

InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, void * interpreterThunk) :
InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
emitBufferManager(allocator, codePageAllocators, /*scriptContext*/ nullptr, _u("Interpreter thunk buffer")),
allocation(nullptr),
allocator(allocator),
thunkCount(0),
thunkBuffer(nullptr),
interpreterThunk(interpreterThunk)
isAsmInterpreterThunk(isAsmInterpreterThunk)
{
}

Expand Down Expand Up @@ -253,6 +253,20 @@ void InterpreterThunkEmitter::NewThunkBlock()
DWORD bufferSize = BlockSize;
DWORD thunkCount = 0;

void * interpreterThunk = nullptr;

// the static interpreter thunk invoked by the dynamic emitted thunk
#ifdef ASMJS_PLAT
if (isAsmInterpreterThunk)
{
interpreterThunk = Js::InterpreterStackFrame::InterpreterAsmThunk;
}
else
#endif
{
interpreterThunk = Js::InterpreterStackFrame::InterpreterThunk;
}

allocation = emitBufferManager.AllocateBuffer(bufferSize, &buffer);
if (!emitBufferManager.ProtectBufferWithExecuteReadWriteForInterpreter(allocation))
{
Expand Down Expand Up @@ -280,7 +294,7 @@ void InterpreterThunkEmitter::NewThunkBlock()

// Copy the thunk buffer and modify it.
js_memcpy_s(currentBuffer, bytesRemaining, InterpreterThunk, HeaderSize);
EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize);
EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize, interpreterThunk);
currentBuffer += HeaderSize;
bytesRemaining -= HeaderSize;

Expand Down Expand Up @@ -359,16 +373,16 @@ void InterpreterThunkEmitter::NewThunkBlock()


#if _M_ARM
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)
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)
{
_Analysis_assume_(thunkSize == HeaderSize);
// Encode MOVW
DWORD lowerThunkBits = (uint32)this->interpreterThunk & 0x0000FFFF;
DWORD lowerThunkBits = (uint32)interpreterThunk & 0x0000FFFF;
DWORD movW = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/1, lowerThunkBits);
Emit(thunkBuffer,ThunkAddressOffset, movW);

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

Expand Down Expand Up @@ -424,7 +438,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
}

#elif _M_ARM64
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)
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)
{
int addrOffset = ThunkAddressOffset;

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

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

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

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

// Encode MOVK (movk x1, #<interpreterThunk 64-48 bits>, lsl #16)
higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF000000000000) >> 48;
higherThunkBits = ((uint64)interpreterThunk & 0xFFFF000000000000) >> 48;
movK = EncodeMove(/*Opcode*/ 0xF2E00000, /*register x1*/1, higherThunkBits); // left shift 48 bits; hw = 03
AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
Emit(thunkBuffer, addrOffset, movK);
Expand Down Expand Up @@ -498,7 +512,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
function->FrameSize = 5; // the number of bytes of stack that is allocated for this function divided by 16
}
#else
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)
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)
{
_Analysis_assume_(thunkSize == HeaderSize);
Emit(thunkBuffer, ThunkAddressOffset, (uintptr_t)interpreterThunk);
Expand Down
6 changes: 3 additions & 3 deletions lib/Backend/InterpreterThunkEmitter.h
Expand Up @@ -61,7 +61,7 @@ class InterpreterThunkEmitter
EmitBufferAllocation *allocation;
SListBase<ThunkBlock> thunkBlocks;
SListBase<ThunkBlock> freeListedThunkBlocks;
void * interpreterThunk; // the static interpreter thunk invoked by the dynamic emitted thunk
bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
BYTE* thunkBuffer;
ArenaAllocator* allocator;
DWORD thunkCount; // Count of thunks available in the current thunk block
Expand Down Expand Up @@ -94,7 +94,7 @@ class InterpreterThunkEmitter

/* ------private helpers -----------*/
void NewThunkBlock();
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);
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);
#if defined(_M_ARM32_OR_ARM64)
DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
Expand All @@ -116,7 +116,7 @@ class InterpreterThunkEmitter
static const uint BlockSize;
static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);

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

BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);

Expand Down
5 changes: 2 additions & 3 deletions lib/Runtime/Base/ScriptContext.cpp
Expand Up @@ -1155,13 +1155,12 @@ if (!sourceList)
}

#if DYNAMIC_INTERPRETER_THUNK
interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
Js::InterpreterStackFrame::InterpreterThunk);
interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators());
#endif

#ifdef ASMJS_PLAT
asmJsInterpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
Js::InterpreterStackFrame::InterpreterAsmThunk);
true);
#endif

JS_ETW(EtwTrace::LogScriptContextLoadEvent(this));
Expand Down
29 changes: 26 additions & 3 deletions lib/Runtime/Library/ArrayBuffer.cpp
Expand Up @@ -878,6 +878,29 @@ namespace Js
#endif
}

// Copy memory from src to dst, truncate if dst smaller, zero extra memory
// if dst larger
static void MemCpyZero(__bcount(dstSize) BYTE* dst, size_t dstSize,
__in_bcount(count) const BYTE* src, size_t count)
{
js_memcpy_s(dst, dstSize, src, min(dstSize, count));
if (dstSize > count)
{
ZeroMemory(dst + count, dstSize - count);
}
}

// Same as realloc but zero newly allocated portion if newSize > oldSize
static BYTE* ReallocZero(BYTE* ptr, size_t oldSize, size_t newSize)
{
BYTE* ptrNew = (BYTE*)realloc(ptr, newSize);
if (ptrNew && newSize > oldSize)
{
ZeroMemory(ptrNew + oldSize, newSize - oldSize);
}
return ptrNew;
}

ArrayBuffer * JavascriptArrayBuffer::TransferInternal(uint32 newBufferLength)
{
ArrayBuffer* newArrayBuffer;
Expand Down Expand Up @@ -940,7 +963,7 @@ namespace Js
recycler->ReportExternalMemoryFailure(newBufferLength);
JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
}
js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
}
}
else
Expand All @@ -949,12 +972,12 @@ namespace Js
{
// we are transferring from an unoptimized buffer, but new length can be optimized, so move to that
newBuffer = (BYTE*)JavascriptArrayBuffer::AllocWrapper(newBufferLength);
js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
}
else if (newBufferLength != this->bufferLength)
{
// both sides will just be regular ArrayBuffer, so realloc
newBuffer = (BYTE*)realloc(this->buffer, newBufferLength);
newBuffer = ReallocZero(this->buffer, this->bufferLength, newBufferLength);
if (!newBuffer)
{
recycler->ReportExternalMemoryFailure(newBufferLength);
Expand Down

0 comments on commit 17f3d4a

Please sign in to comment.