Ver código fonte

[1.2>1.3] Changes addressing CVE_2016-3382, CVE-2016-3385, CVE-2016-3386, CVE-2016-3389,
CVE-2016-3390, CVE-2016-7189, and a mitigation of a CFG bypass.

Details:

Calls that target the external thunks should decrement the callinfo param count if the extra-param flag is set.

Don't optimize spread operation in a parameter list if the array we're spreading may have gaps. Accessing an element in the prototype chain may have side-effects that invalidate the optimization.

Port disabling of UT that times out

Type confusion in JavascriptArray
Type confusion in JavascriptArray::TemplatedGetItem()
Array.prototype.join()
Array.prototype.indexOf()
Array.prototype.lastIndexOf()

Type confusion in JavascriptArray::TemplatedGetItem()
Function.prototype.apply()

Type confusion in JavascriptArray::MapHelper()
Array.prototype.map()

CRC computation and validation for the encoder buffer

Premise:
Encoder phase takes longer time for a relatively larger function. So the buffer to which we write the encoded bytes will be RWX all the while till it completes the encoding.
This time is big enough for the main thread to write in this region.
We then transfer the data to the final buffer and execute the code in the buffer(which, now, also contains the modified code).

Mitigation:
We can check the integrity of the buffer data using CRC32(Cyclic Redundancy Check) at suitable spots.
Following is the mechanism for validation:
- Start with a random CRC seed.
- Compute the CRC1 during the encoding phase.
- Validate the CRC1 during branch shortening.
- Compute CRC2 during branch shortening.
- Validate CRC2 (or CRC1, if branch shortening didn't happen) after copying the entire buffer to the final RX buffer.
- Finally, register the entry point as a valid CFG target.

CRC32 Intrinsic instruction is available only on SSE4 and above. Hence for other cases, CRC32 algorithm is implemented.
We were storing LabelInstr* directly in the encoded bytes - Moved it to be stored in a property. - To enable CRC calculation.

Perf results:
No visible changes in console benchmark run (desktop and low-memory device).

Fixes to use-after-free in Globopt, Lowering.

Tail duplication consists of the following code :

branchEntry->ReplaceTarget(mergeLabel, tailBranch->GetTarget());
instr = branchEntry;

branchEntry is a reference to a SList node that can get deleted within
ReplaceTarget function. Subsequent use of the same reference is referring
to a freed value. Fix by caching branchEntry before ReplaceTarget.

Lowering floor builtin code creates a 'zero' MemRefOpnd, which gets passed
through Legalizer, which can delete the Opnd. Subsequent uses of the
MemRefOpnd in Lowering refers to a freed value. This is fixed by
AutoReuseOpnd which will avoid this scenario.

Correcting the version check for SSE4

Paul Leathers 9 anos atrás
pai
commit
6da3d9fafb

+ 8 - 0
THIRD-PARTY-NOTICES.txt

@@ -229,3 +229,11 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 DEALINGS IN THE SOFTWARE.
 
 ---------------------------------------------------------------------------
+---------------------------------------------
+
+CRC.h
+
+COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or code or tables 
+extracted from it, as desired without restriction. 
+
+

+ 1 - 0
jenkins/check_copyright.sh

@@ -37,6 +37,7 @@ git diff --name-only `git merge-base origin/master HEAD` HEAD |
     grep -v -E 'bin/NativeTests/Scripts/splay.js$' |
     grep -v -E 'pal/.*' |
     grep -v -E 'libChakraCoreLib.version|ch.version' |
+    grep -v -E 'lib/Backend/CRC.h' |
     xargs -I % sh -c "echo 'Check Copyright > Checking %'; python jenkins/check_copyright.py % > $ERRFILETEMP || cat $ERRFILETEMP >> $ERRFILE"
 
 if [ -e $ERRFILE ]; then # if error file exists then there were errors

+ 51 - 0
lib/Backend/CRC.h

@@ -0,0 +1,51 @@
+/*
+*   CRC32 code derived from work by Gary S. Brown.
+*/
+
+/*
+* Pre-populated Table used for calculating CRC32.
+*/
+static const unsigned int crc_32_tab[] =
+{
+    0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL, 0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+    0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+    0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+    0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, 0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+    0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+    0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+    0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+    0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+    0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL, 0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+    0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+    0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+    0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+    0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+    0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, 0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+    0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+    0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+    0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL, 0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+    0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+    0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, 0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+    0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+    0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+    0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+    0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+    0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, 0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+    0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL, 0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+    0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+    0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+    0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+    0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, 0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+    0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+    0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+    0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
+};
+
+static unsigned int CalculateCRC32(unsigned int bufferCRC, size_t data)
+{
+    /* update running CRC calculation with contents of a buffer */
+
+    bufferCRC = bufferCRC ^ 0xffffffffL;
+    bufferCRC = crc_32_tab[(bufferCRC ^ data) & 0xFF] ^ (bufferCRC >> 8);
+    return (bufferCRC ^ 0xffffffffL);
+}

+ 6 - 1
lib/Backend/Chakra.Backend.vcxproj

@@ -410,10 +410,15 @@
     <None Include="Opnd.inl" />
     <None Include="Sym.inl" />
   </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="CRC.h">
+      <FileType>CppCode</FileType>
+    </ClInclude>
+  </ItemGroup>
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
     <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
     <Import Project="$(BuildConfig_ARMASM_Path)armasm.targets" />
   </ImportGroup>
-</Project>
+</Project>

+ 1 - 0
lib/Backend/Chakra.Backend.vcxproj.filters

@@ -300,6 +300,7 @@
     </ClInclude>
     <ClInclude Include="IRBaseTypeList.h" />
     <ClInclude Include="SwitchIRBuilder.h" />
+    <ClInclude Include="CRC.h" />
   </ItemGroup>
   <ItemGroup>
     <MASM Include="$(MSBuildThisFileDirectory)amd64\LinearScanMdA.asm">

+ 231 - 10
lib/Backend/Encoder.cpp

@@ -3,6 +3,7 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "Backend.h"
+#include "CRC.h"
 
 ///----------------------------------------------------------------------------
 ///
@@ -71,6 +72,17 @@ Encoder::Encode()
 #endif
     bool isCallInstr = false;
 
+    // CRC Check to ensure the integrity of the encoded bytes.
+    uint initialCRCSeed = 0;
+    errno_t err = rand_s(&initialCRCSeed);
+
+    if (err != 0)
+    {
+        Fatal();
+    }
+
+    uint bufferCRC = initialCRCSeed;  
+
     FOREACH_INSTR_IN_FUNC(instr, m_func)
     {
         Assert(Lowerer::ValidOpcodeAfterLower(instr, m_func));
@@ -140,7 +152,8 @@ Encoder::Encode()
 #if defined(_M_ARM32_OR_ARM64)
                         encoderMD->AddLabelReloc((byte*) offset);
 #else
-                        encoderMD->AppendRelocEntry(RelocTypeLabelUse, (void*) (offset));
+                        encoderMD->AppendRelocEntry(RelocTypeLabelUse, (void*) (offset), *(IR::LabelInstr**)(offset));
+                        *((size_t*)offset) = 0;
 #endif
                     });
                 }
@@ -191,6 +204,9 @@ Encoder::Encode()
             }
 
             count = m_encoderMD.Encode(instr, m_pc, m_encodeBuffer);
+#if defined(_M_IX86) || defined(_M_X64)
+            bufferCRC = CalculateCRC(bufferCRC, count, m_pc);
+#endif
 
 #if DBG_DUMP
             if (PHASE_TRACE(Js::EncoderPhase, this->m_func))
@@ -231,15 +247,21 @@ Encoder::Encode()
             Fatal();
         }
     } NEXT_INSTR_IN_FUNC;
-
+    
     ptrdiff_t codeSize = m_pc - m_encodeBuffer + totalJmpTableSizeInBytes;
 
-#if defined(_M_IX86) || defined(_M_X64)
     BOOL isSuccessBrShortAndLoopAlign = false;
+
+#if defined(_M_IX86) || defined(_M_X64)
     // Shorten branches. ON by default
     if (!PHASE_OFF(Js::BrShortenPhase, m_func))
     {
-        isSuccessBrShortAndLoopAlign = ShortenBranchesAndLabelAlign(&m_encodeBuffer, &codeSize);
+        uint brShortenedbufferCRC = initialCRCSeed;
+        isSuccessBrShortAndLoopAlign = ShortenBranchesAndLabelAlign(&m_encodeBuffer, &codeSize, &brShortenedbufferCRC, bufferCRC, totalJmpTableSizeInBytes);
+        if (isSuccessBrShortAndLoopAlign)
+        {
+            bufferCRC = brShortenedbufferCRC;
+        }
     }
 #endif
 #if DBG_DUMP | defined(VTUNE_PROFILING)
@@ -291,10 +313,14 @@ Encoder::Encode()
     });
 
     // Relocs
-    m_encoderMD.ApplyRelocs((size_t) workItem->GetCodeAddress());
+    m_encoderMD.ApplyRelocs((size_t) workItem->GetCodeAddress(), codeSize, &bufferCRC, isSuccessBrShortAndLoopAlign);
 
     workItem->RecordNativeCode(m_func, m_encodeBuffer);
 
+#if defined(_M_IX86) || defined(_M_X64)
+    ValidateCRCOnFinalBuffer((BYTE*)workItem->GetCodeAddress(), codeSize, totalJmpTableSizeInBytes, m_encodeBuffer, initialCRCSeed, bufferCRC, isSuccessBrShortAndLoopAlign);
+#endif
+
     m_func->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG((PVOID) workItem->GetCodeAddress());
 
 #ifdef _M_X64
@@ -575,7 +601,8 @@ void Encoder::TryCopyAndAddRelocRecordsForSwitchJumpTableEntries(BYTE *codeStart
 #if defined(_M_ARM32_OR_ARM64)
             encoderMD->AddLabelReloc((byte*) addressOfJmpTableEntry);
 #else
-            encoderMD->AppendRelocEntry(RelocTypeLabelUse, addressOfJmpTableEntry);
+            encoderMD->AppendRelocEntry(RelocTypeLabelUse, addressOfJmpTableEntry, *(IR::LabelInstr**)addressOfJmpTableEntry);
+            *((size_t*)addressOfJmpTableEntry) = 0;
 #endif
         }
 
@@ -619,6 +646,156 @@ void Encoder::RecordInlineeFrame(Func* inlinee, uint32 currentOffset)
     }
 }
 
+#if defined(_M_IX86) || defined(_M_X64)
+/*
+*   ValidateCRCOnFinalBuffer
+*       - Validates the CRC that is last computed (could be either the one after BranchShortening or after encoding itself)
+*       - We calculate the CRC for jump table and dictionary after computing the code section.
+*       - Also, all reloc data are computed towards the end - after computing the code section - because we don't have to deal with the changes relocs while operating on the code section.
+*       - The version of CRC that we are validating with, doesn't have Relocs applied but the final buffer does - So we have to make adjustments while calculating the final buffer's CRC.
+*/
+void Encoder::ValidateCRCOnFinalBuffer(_In_reads_bytes_(finalCodeSize) BYTE * finalCodeBufferStart, size_t finalCodeSize, size_t jumpTableSize, _In_reads_bytes_(finalCodeSize) BYTE * oldCodeBufferStart, uint initialCrcSeed, uint bufferCrcToValidate, BOOL isSuccessBrShortAndLoopAlign)
+{
+    RelocList * relocList = m_encoderMD.GetRelocList();
+
+    BYTE * currentStartAddress = finalCodeBufferStart;
+    BYTE * currentEndAddress = nullptr;
+    size_t crcSizeToCompute = 0;
+
+    size_t finalCodeSizeWithoutJumpTable = finalCodeSize - jumpTableSize;
+
+    uint finalBufferCRC = initialCrcSeed;
+
+    BYTE * oldPtr = nullptr;
+
+    if (relocList != nullptr)
+    {
+        for (int index = 0; index < relocList->Count(); index++)
+        {
+            EncodeRelocAndLabels * relocTuple = &relocList->Item(index);
+
+            //We will deal with the jump table and dictionary entries along with other reloc records in ApplyRelocs()
+            if ((BYTE*)m_encoderMD.GetRelocBufferAddress(relocTuple) >= oldCodeBufferStart && (BYTE*)m_encoderMD.GetRelocBufferAddress(relocTuple) < (oldCodeBufferStart + finalCodeSizeWithoutJumpTable))
+            {
+                BYTE* finalBufferRelocTuplePtr = (BYTE*)m_encoderMD.GetRelocBufferAddress(relocTuple) - oldCodeBufferStart + finalCodeBufferStart;
+                Assert(finalBufferRelocTuplePtr >= finalCodeBufferStart && finalBufferRelocTuplePtr < (finalCodeBufferStart + finalCodeSizeWithoutJumpTable));
+                uint relocDataSize = m_encoderMD.GetRelocDataSize(relocTuple);
+                if (relocDataSize != 0)
+                {
+                    AssertMsg(oldPtr == nullptr || oldPtr < finalBufferRelocTuplePtr, "Assumption here is that the reloc list is strictly increasing in terms of bufferAddress");
+                    oldPtr = finalBufferRelocTuplePtr;
+
+                    currentEndAddress = finalBufferRelocTuplePtr;
+                    crcSizeToCompute = currentEndAddress - currentStartAddress;
+                    
+                    Assert(currentEndAddress >= currentStartAddress);
+
+                    finalBufferCRC = CalculateCRC(finalBufferCRC, crcSizeToCompute, currentStartAddress);
+                    for (uint i = 0; i < relocDataSize; i++)
+                    {
+                        finalBufferCRC = CalculateCRC(finalBufferCRC, 0);
+                    }
+                    currentStartAddress = currentEndAddress + relocDataSize;
+                }
+            }
+        }
+    }
+
+    currentEndAddress = finalCodeBufferStart + finalCodeSizeWithoutJumpTable;
+    crcSizeToCompute = currentEndAddress - currentStartAddress;
+
+    Assert(currentEndAddress >= currentStartAddress);
+
+    finalBufferCRC = CalculateCRC(finalBufferCRC, crcSizeToCompute, currentStartAddress);
+
+    //Include all offsets from the reloc records to the CRC.
+    m_encoderMD.ApplyRelocs((size_t)finalCodeBufferStart, finalCodeSize, &finalBufferCRC, isSuccessBrShortAndLoopAlign, true);
+
+    if (finalBufferCRC != bufferCrcToValidate)
+    {
+        Assert(false);
+        Fatal();
+    }
+}
+#endif
+
+/*
+*   EnsureRelocEntryIntegrity
+*       - We compute the target address as the processor would compute it and check if the target is within the final buffer's bounds.
+*       - For relative addressing, Target = current m_pc + offset
+*       - For absolute addressing, Target = direct address
+*/
+void Encoder::EnsureRelocEntryIntegrity(size_t newBufferStartAddress, size_t codeSize, size_t oldBufferAddress, size_t relocAddress, uint offsetBytes, ptrdiff_t opndData, bool isRelativeAddr)
+{
+    size_t targetBrAddress = 0;
+    size_t newBufferEndAddress = newBufferStartAddress + codeSize;
+    
+    //Handle Dictionary addresses here - The target address will be in the dictionary.
+    if (relocAddress < oldBufferAddress || relocAddress >= (oldBufferAddress + codeSize))
+    {
+        targetBrAddress = (size_t)(*(size_t*)relocAddress);
+    }
+    else
+    {
+        size_t newBufferRelocAddr = relocAddress - oldBufferAddress + newBufferStartAddress;
+
+        if (isRelativeAddr)
+        {
+            targetBrAddress = (size_t)newBufferRelocAddr + offsetBytes + opndData;
+        }
+        else  // Absolute Address
+        {
+            targetBrAddress = (size_t)opndData;
+        }
+    }
+
+    if (targetBrAddress < newBufferStartAddress || targetBrAddress >= newBufferEndAddress)
+    {
+        Assert(false);
+        Fatal();
+    }
+}
+
+uint Encoder::CalculateCRC(uint bufferCRC, size_t data)
+{
+#if defined(_M_IX86)
+    if (AutoSystemInfo::Data.SSE4_2Available())
+    {
+        return _mm_crc32_u32(bufferCRC, data);
+    }
+#elif defined(_M_X64)
+    if (AutoSystemInfo::Data.SSE4_2Available())
+    {
+        //CRC32 always returns a 32-bit result
+        return (uint)_mm_crc32_u64(bufferCRC, data);
+    }
+#endif
+
+    return CalculateCRC32(bufferCRC, data);
+}
+
+uint Encoder::CalculateCRC(uint bufferCRC, size_t count, _In_reads_bytes_(count) void * buffer)
+{
+    for (uint index = 0; index < count; index++)
+    {
+        bufferCRC = CalculateCRC(bufferCRC, *((BYTE*)buffer + index));
+    }
+    return bufferCRC;
+}
+
+void Encoder::ValidateCRC(uint bufferCRC, uint initialCRCSeed, _In_reads_bytes_(count) void* buffer, size_t count)
+{
+    uint validationCRC = initialCRCSeed;
+
+    validationCRC = CalculateCRC(validationCRC, count, buffer);
+
+    if (validationCRC != bufferCRC)
+    {
+        //TODO: This throws internal error. Is this error type, Fine?
+        Fatal();
+    }
+}
+
 #if defined(_M_IX86) || defined(_M_X64)
 ///----------------------------------------------------------------------------
 ///
@@ -628,7 +805,7 @@ void Encoder::RecordInlineeFrame(Func* inlinee, uint32 currentOffset)
 /// Also align LoopTop Label and TryCatchLabel
 ///----------------------------------------------------------------------------
 BOOL
-Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
+Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize, uint * pShortenedBufferCRC, uint bufferCrcToValidate, size_t jumpTableSize)
 {
 #ifdef  ENABLE_DEBUG_CONFIG_OPTIONS
     static uint32 globalTotalBytesSaved = 0, globalTotalBytesWithoutShortening = 0;
@@ -795,6 +972,8 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
     // Next, we re-write the code to shorten the BRs and adjust relocList offsets to point to new buffer.
     // We also write NOPs for aligned loops.
     BYTE* tmpBuffer = AnewArray(m_tempAlloc, BYTE, newCodeSize);
+    
+    uint srcBufferCrc = *pShortenedBufferCRC;   //This has the intial Random CRC seed to start with.
 
     // start copying to new buffer
     // this can possibly be done during fixing, but there is no evidence it is an overhead to justify the complexity.
@@ -853,6 +1032,10 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
             AnalysisAssert(dst_size >= src_size);
 
             memcpy_s(dst_p, dst_size, from, src_size);
+
+            srcBufferCrc = CalculateCRC(srcBufferCrc, (BYTE*)reloc.m_origPtr - from + 4, from);
+            *pShortenedBufferCRC = CalculateCRC(*pShortenedBufferCRC, src_size, dst_p);
+
             dst_p += src_size;
             dst_size -= src_size;
 
@@ -860,10 +1043,29 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
             // write new opcode
             AnalysisAssert(dst_p < tmpBuffer + newCodeSize);
             *dst_p = (*opcodeByte == 0xe9) ? (BYTE)0xeb : (BYTE)(*opcodeByte - 0x10);
+            *(dst_p + 1) = 0;   // imm8
+
+            *pShortenedBufferCRC = CalculateCRC(*pShortenedBufferCRC, 2, dst_p);
             dst_p += 2; // 1 byte for opcode + 1 byte for imm8
             dst_size -= 2;
             from = (BYTE*)reloc.m_origPtr + 4;
         }
+        else if (reloc.m_type == RelocTypeInlineeEntryOffset)
+        {
+            to = (BYTE*)reloc.m_origPtr - 1;
+            CopyPartialBufferAndCalculateCRC(&dst_p, dst_size, from, to, pShortenedBufferCRC);
+
+            *(size_t*)dst_p = reloc.GetInlineOffset();
+
+            *pShortenedBufferCRC = CalculateCRC(*pShortenedBufferCRC, sizeof(size_t), dst_p);
+
+            dst_p += sizeof(size_t);
+            dst_size -= sizeof(size_t);
+
+            srcBufferCrc = CalculateCRC(srcBufferCrc, (BYTE*)reloc.m_origPtr + sizeof(size_t) - from , from);
+
+            from = (BYTE*)reloc.m_origPtr + sizeof(size_t);
+        }
         // insert NOPs for aligned labels
         else if ((!PHASE_OFF(Js::LoopAlignPhase, m_func) && reloc.isAlignedLabel()) && reloc.getLabelNopCount() > 0)
         {
@@ -874,7 +1076,9 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
             AssertMsg((((uint32)(label->GetPC() - buffStart)) & 0xf) == 0, "Misaligned Label");
 
             to = reloc.getLabelOrigPC() - 1;
-            CopyPartialBuffer(&dst_p, dst_size, from, to);
+            
+            CopyPartialBufferAndCalculateCRC(&dst_p, dst_size, from, to, pShortenedBufferCRC);
+            srcBufferCrc = CalculateCRC(srcBufferCrc, to - from + 1, from);
 
 #ifdef  ENABLE_DEBUG_CONFIG_OPTIONS
             if (PHASE_TRACE(Js::LoopAlignPhase, this->m_func))
@@ -886,17 +1090,28 @@ Encoder::ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize)
                 Output::Flush();
             }
 #endif
+            BYTE * tmpDst_p = dst_p;
             InsertNopsForLabelAlignment(nop_count, &dst_p);
+            *pShortenedBufferCRC = CalculateCRC(*pShortenedBufferCRC, nop_count, tmpDst_p);
 
             dst_size -= nop_count;
             from = to + 1;
         }
     }
     // copy last chunk
-    CopyPartialBuffer(&dst_p, dst_size, from, buffStart + *codeSize - 1);
+    //Exclude jumpTable content from CRC calculation. 
+    //Though jumpTable is not part of the encoded bytes, codeSize has jumpTableSize included in it.
+    CopyPartialBufferAndCalculateCRC(&dst_p, dst_size, from, buffStart + *codeSize - 1, pShortenedBufferCRC, jumpTableSize);
+    srcBufferCrc = CalculateCRC(srcBufferCrc, buffStart + *codeSize - from - jumpTableSize, from);
 
     m_encoderMD.UpdateRelocListWithNewBuffer(relocList, tmpBuffer, buffStart, buffEnd);
 
+    if (srcBufferCrc != bufferCrcToValidate)
+    {
+        Assert(false);
+        Fatal();
+    }
+
     // switch buffers
     *codeStart = tmpBuffer;
     *codeSize = newCodeSize;
@@ -909,13 +1124,19 @@ BYTE Encoder::FindNopCountFor16byteAlignment(size_t address)
     return (16 - (BYTE) (address & 0xf)) % 16;
 }
 
-void Encoder::CopyPartialBuffer(BYTE ** ptrDstBuffer, size_t &dstSize, BYTE * srcStart, BYTE * srcEnd)
+void Encoder::CopyPartialBufferAndCalculateCRC(BYTE ** ptrDstBuffer, size_t &dstSize, BYTE * srcStart, BYTE * srcEnd, uint* pBufferCRC, size_t jumpTableSize)
 {
     BYTE * destBuffer = *ptrDstBuffer;
 
     size_t srcSize = srcEnd - srcStart + 1;
     Assert(dstSize >= srcSize);
     memcpy_s(destBuffer, dstSize, srcStart, srcSize);
+
+    Assert(srcSize >= jumpTableSize);
+
+    //Exclude the jump table content (which is at the end of the buffer) for calculating CRC - at this point.
+    *pBufferCRC = CalculateCRC(*pBufferCRC, srcSize - jumpTableSize, destBuffer);
+
     *ptrDstBuffer += srcSize;
     dstSize -= srcSize;
 }

+ 10 - 2
lib/Backend/Encoder.h

@@ -49,15 +49,23 @@ private:
 #if defined(_M_IX86) || defined(_M_X64)
     InlineeFrameRecords *m_inlineeFrameRecords;
 
-    BOOL            ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize);
+    BOOL            ShortenBranchesAndLabelAlign(BYTE **codeStart, ptrdiff_t *codeSize, uint * brShortenedBufferCRC, uint bufferCrcToValidate, size_t jumpTableSize);
     void            revertRelocList();
     template <bool restore> void  CopyMaps(OffsetList **m_origInlineeFrameRecords, OffsetList **m_origInlineeFrameMap, OffsetList **m_origPragmaInstrToRecordOffset, OffsetList **m_origOffsetBuffer);
 #endif
     void            InsertNopsForLabelAlignment(int nopCount, BYTE ** pDstBuffer);
-    void            CopyPartialBuffer(BYTE ** ptrDstBuffer, size_t &dstSize, BYTE * srcStart, BYTE * srcEnd);
+    void            CopyPartialBufferAndCalculateCRC(BYTE ** ptrDstBuffer, size_t &dstSize, BYTE * srcStart, BYTE * srcEnd, uint* pBufferCRC, size_t jumpTableSize = 0);
     BYTE            FindNopCountFor16byteAlignment(size_t address);
 
     uint32          GetCurrentOffset() const;
     void            TryCopyAndAddRelocRecordsForSwitchJumpTableEntries(BYTE *codeStart, size_t codeSize, JmpTableList * jumpTableListForSwitchStatement, size_t totalJmpTableSizeInBytes);
+
+    void            ValidateCRC(uint bufferCRC, uint initialCRCSeed, _In_reads_bytes_(count) void* buffer, size_t count);
+    static uint     CalculateCRC(uint bufferCRC, size_t count, _In_reads_bytes_(count) void * buffer);
+    static uint     CalculateCRC(uint bufferCRC, size_t data);
+    static void     EnsureRelocEntryIntegrity(size_t newBufferStartAddress, size_t codeSize, size_t oldBufferAddress, size_t relocAddress, uint offsetBytes, ptrdiff_t opndData, bool isRelativeAddr = true);
+#if defined(_M_IX86) || defined(_M_X64)
+    void            ValidateCRCOnFinalBuffer(_In_reads_bytes_(finalCodeSize) BYTE * finalCodeBufferStart, size_t finalCodeSize, size_t jumpTableSize, _In_reads_bytes_(finalCodeSize) BYTE * oldCodeBufferStart, uint initialCrcSeed, uint bufferCrcToValidate, BOOL isSuccessBrShortAndLoopAlign);
+#endif
 };
 

+ 2 - 1
lib/Backend/GlobOpt.cpp

@@ -940,9 +940,10 @@ GlobOpt::TryTailDup(IR::BranchInstr *tailBranch)
             {
                 branchEntry->InsertBefore(instr->Copy());
             }
-            branchEntry->ReplaceTarget(mergeLabel, tailBranch->GetTarget());
 
             instr = branchEntry;
+            branchEntry->ReplaceTarget(mergeLabel, tailBranch->GetTarget());
+
             while(!instr->IsLabelInstr())
             {
                 instr = instr->m_prev;

+ 1 - 1
lib/Backend/LowerMDShared.cpp

@@ -8898,7 +8898,7 @@ void LowererMD::GenerateFastInlineBuiltInCall(IR::Instr* instr, IR::JnHelperMeth
                 Assert(src->IsFloat32());
                 zero = IR::MemRefOpnd::New((float*)&Js::JavascriptNumber::k_Float32Zero, TyFloat32, this->m_func, IR::AddrOpndKindDynamicFloatRef);
             }
-
+            IR::AutoReuseOpnd autoReuseZero(zero, this->m_func);
             IR::LabelInstr * skipRoundSd = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
 
             if(instr->m_opcode == Js::OpCode::InlineMathRound)

+ 72 - 9
lib/Backend/amd64/EncoderMD.cpp

@@ -506,8 +506,8 @@ intConst:
         break;
 
     case IR::OpndKindLabel:
-        value = (size_t)opnd->AsLabelOpnd()->GetLabel();
-        AppendRelocEntry(RelocTypeLabelUse, (void*) m_pc, nullptr);
+        value = 0;
+        AppendRelocEntry(RelocTypeLabelUse, (void*) m_pc, opnd->AsLabelOpnd()->GetLabel());
         break;
 
     default:
@@ -1473,7 +1473,7 @@ EncoderMD::FixRelocListEntry(uint32 index, int totalBytesSaved, BYTE *buffStart,
                 uint32 count = field & 0xf;
 
                 AssertMsg(offset < (size_t)(buffEnd - buffStart), "Inlinee entry offset out of range");
-                *((size_t*) relocRecord.m_origPtr) = ((offset - totalBytesSaved) << 4) | count;
+                relocRecord.SetInlineOffset(((offset - totalBytesSaved) << 4) | count);
             }
             // adjust the ptr to the buffer itself
             relocRecord.m_ptr = (BYTE*) relocRecord.m_ptr - totalBytesSaved;
@@ -1531,7 +1531,7 @@ EncoderMD::FixMaps(uint32 brOffset, uint32 bytesSaved, uint32 *inlineeFrameRecor
 ///----------------------------------------------------------------------------
 
 void
-EncoderMD::ApplyRelocs(size_t codeBufferAddress_)
+EncoderMD::ApplyRelocs(size_t codeBufferAddress_, size_t codeSize, uint * bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation)
 {
     if (m_relocList == nullptr)
     {
@@ -1565,22 +1565,52 @@ EncoderMD::ApplyRelocs(size_t codeBufferAddress_)
                     // short branch
                     pcrel = (uint32)(labelInstr->GetPC() - ((BYTE*)reloc->m_ptr + 1));
                     AssertMsg((int32)pcrel >= -128 && (int32)pcrel <= 127, "Offset doesn't fit in imm8.");
-                    *(BYTE*)relocAddress = (BYTE)pcrel;
+                    if (!isFinalBufferValidation)
+                    {
+                        Assert(*(BYTE*)relocAddress == 0);
+                        *(BYTE*)relocAddress = (BYTE)pcrel;
+                    }
+                    else
+                    {
+                        Encoder::EnsureRelocEntryIntegrity(codeBufferAddress_, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(BYTE), (ptrdiff_t)labelInstr->GetPC() - ((ptrdiff_t)reloc->m_ptr + 1));
+                    }
                 }
                 else
                 {
                     pcrel = (uint32)(labelInstr->GetPC() - ((BYTE*)reloc->m_ptr + 4));
-                    *(uint32 *)relocAddress = pcrel;
+                    if (!isFinalBufferValidation)
+                    {
+                        Assert(*(uint32*)relocAddress == 0);
+                        *(uint32 *)relocAddress = pcrel;
+                    }
+                    else
+                    {
+                        Encoder::EnsureRelocEntryIntegrity(codeBufferAddress_, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(uint32), (ptrdiff_t)labelInstr->GetPC() - ((ptrdiff_t)reloc->m_ptr + 4));
+                    }
                 }
-                break;
 
+                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, pcrel);
+
+                break;
             }
 
         case RelocTypeLabelUse:
             {
-                IR::LabelInstr *labelInstr = *(IR::LabelInstr**)relocAddress;
+                IR::LabelInstr *labelInstr = reloc->getBrTargetLabel();
                 AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?");
-                *(size_t *)relocAddress = (size_t)(labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress_);
+
+                size_t offset = (size_t)(labelInstr->GetPC() - m_encoder->m_encodeBuffer);
+                size_t targetAddress = (size_t)(offset + codeBufferAddress_);
+                if (!isFinalBufferValidation)
+                {
+                    Assert(*(size_t *)relocAddress == 0);
+                    *(size_t *)relocAddress = targetAddress;
+                }
+                else
+                {
+                    Encoder::EnsureRelocEntryIntegrity(codeBufferAddress_, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(size_t), targetAddress, false);
+                }
+                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeLabel:
@@ -1593,6 +1623,39 @@ EncoderMD::ApplyRelocs(size_t codeBufferAddress_)
     }
 }
 
+uint 
+EncoderMD::GetRelocDataSize(EncodeRelocAndLabels *reloc)
+{
+    switch (reloc->m_type)
+    {
+        case RelocTypeBranch:
+        {
+            if (reloc->isShortBr())
+            {
+                return sizeof(BYTE);
+            }
+            else
+            {
+                return sizeof(uint);
+            }
+        }
+        case RelocTypeLabelUse:
+        {
+            return sizeof(size_t);
+        }
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+BYTE * 
+EncoderMD::GetRelocBufferAddress(EncodeRelocAndLabels * reloc)
+{
+    return (BYTE*)reloc->m_ptr;
+}
+
 #ifdef DBG
 ///----------------------------------------------------------------------------
 ///

+ 24 - 14
lib/Backend/amd64/EncoderMD.h

@@ -33,7 +33,7 @@ private:
     {
         IR::LabelInstr* m_labelInstr;        // ptr to Br Label
         BYTE            m_nopCount;
-        uint64          m_origInlineeOffset;
+        uint64          m_InlineeOffset;
     };
     bool                m_isShortBr;
 
@@ -42,6 +42,9 @@ public:
     {
         m_type = type;
         m_ptr = ptr;
+        m_InlineeOffset = 0;
+        m_isShortBr = false;
+
         if (type == RelocTypeLabel)
         {
             // preserve original PC for labels
@@ -51,16 +54,17 @@ public:
         else
         {
             m_origPtr = ptr;
-            // in case we have to revert, we need to store original offset in code buffer
-            if (type == RelocTypeInlineeEntryOffset)
+            
+            if (type == RelocTypeBranch)
             {
-                m_origInlineeOffset = *((uint64*)m_origPtr);
+                Assert(labelInstr);
+                m_labelInstr = labelInstr;
+                m_isShortBr = false;
             }
-            else if (type == RelocTypeBranch)
+            else if (type == RelocTypeLabelUse)
             {
                 Assert(labelInstr);
                 m_labelInstr = labelInstr;
-                m_isShortBr = false;
             }
         }
     }
@@ -77,12 +81,6 @@ public:
             return;
         }
 
-        // re-write original inlinee offset to code buffer
-        if (m_type == RelocTypeInlineeEntryOffset)
-        {
-            *(uint64*) m_origPtr = m_origInlineeOffset;
-        }
-
         if (m_type == RelocTypeBranch)
         {
             m_isShortBr = false;
@@ -99,7 +97,7 @@ public:
 
     IR::LabelInstr *    getBrTargetLabel()  const
     {
-        Assert(m_type == RelocTypeBranch && m_labelInstr);
+        Assert((m_type == RelocTypeBranch || m_type == RelocTypeLabelUse) && m_labelInstr);
         return m_labelInstr;
     }
     IR::LabelInstr *    getLabel()  const
@@ -153,6 +151,16 @@ public:
             getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) >= -128 &&
             getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) <= 127;
     }
+
+    uint64 GetInlineOffset()
+    {
+        return m_InlineeOffset;
+    }
+
+    void SetInlineOffset(uint64 offset)
+    {
+        m_InlineeOffset = offset;
+    }
 };
 
 
@@ -173,7 +181,9 @@ public:
     EncoderMD(Func * func) : m_func(func) {}
     ptrdiff_t       Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr);
     void            Init(Encoder *encoder);
-    void            ApplyRelocs(size_t codeBufferAddress);
+    void            ApplyRelocs(size_t codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false);
+    uint            GetRelocDataSize(EncodeRelocAndLabels *reloc);
+    BYTE *          GetRelocBufferAddress(EncodeRelocAndLabels * reloc);
     void            EncodeInlineeCallInfo(IR::Instr *instr, uint32 offset);
     static bool     TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
     static bool     TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd);

+ 1 - 1
lib/Backend/arm/EncoderMD.cpp

@@ -2263,7 +2263,7 @@ EncoderMD::BaseAndOffsetFromSym(IR::SymOpnd *symOpnd, RegNum *pBaseReg, int32 *p
 /// before we copy the contents of the temporary buffer to the target buffer.
 ///----------------------------------------------------------------------------
 void
-EncoderMD::ApplyRelocs(uint32 codeBufferAddress)
+EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation)
 {
     for (EncodeReloc *reloc = m_relocList; reloc; reloc = reloc->m_next)
     {

+ 1 - 1
lib/Backend/arm/EncoderMD.h

@@ -55,7 +55,7 @@ public:
     EncoderMD(Func * func) : m_func(func), consecutiveThumbInstrCount(0) { }
     ptrdiff_t       Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr);
     void            Init(Encoder *encoder);
-    void            ApplyRelocs(uint32 codeBufferAddress);
+    void            ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false);
     static bool     TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
     static bool     TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
     const BYTE      GetRegEncode(IR::RegOpnd *regOpnd);

+ 1 - 1
lib/Backend/arm64/EncoderMD.h

@@ -53,7 +53,7 @@ public:
     EncoderMD(Func * func) { }
     ptrdiff_t       Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr) { __debugbreak(); return 0; }
     void            Init(Encoder *encoder) { __debugbreak(); }
-    void            ApplyRelocs(size_t codeBufferAddress) { __debugbreak(); }
+    void            ApplyRelocs(size_t codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false) { __debugbreak(); }
     static bool     TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd) { __debugbreak(); return 0; }
     static bool     TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd) { __debugbreak(); return 0; }
     const BYTE      GetRegEncode(IR::RegOpnd *regOpnd) { __debugbreak(); return 0; }

+ 90 - 28
lib/Backend/i386/EncoderMD.cpp

@@ -481,7 +481,6 @@ void
 EncoderMD::EmitCondBranch(IR::BranchInstr * branchInstr)
 {
     IR::LabelInstr * labelInstr;
-
     // TODO: Make this more table-driven by mapping opcodes to condcodes.
     // (Will become more useful when we're emitting short branches as well.)
 
@@ -556,12 +555,10 @@ EncoderMD::EmitCondBranch(IR::BranchInstr * branchInstr)
         break;
     }
 
-    AppendRelocEntry(RelocTypeBranch, (void*) m_pc);
-
-    // Save the target LabelInstr's address in the encoder buffer itself, using the 4-byte
-    // pcrel field of the branch instruction. This only works for long branches, obviously.
     labelInstr = branchInstr->GetTarget();
-    this->EmitConst((uint32)labelInstr, MachInt);
+    AppendRelocEntry(RelocTypeBranch, (void*) m_pc, labelInstr);
+
+    this->EmitConst(0, MachInt);
 }
 
 ///----------------------------------------------------------------------------
@@ -764,8 +761,8 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
             this->EmitModRM(instr, opr1, this->GetOpcodeByte2(instr) >> 3);
             if (opr2->IsLabelOpnd())
             {
-                AppendRelocEntry( RelocTypeLabelUse, (void*) m_pc);
-                this->EmitConst((uint32)opr2->AsLabelOpnd()->GetLabel(), 4);
+                AppendRelocEntry( RelocTypeLabelUse, (void*) m_pc, opr2->AsLabelOpnd()->GetLabel());
+                this->EmitConst(0, 4);
             }
             else
             {
@@ -874,8 +871,8 @@ modrm:
                 {
                     continue;
                 }
-                AppendRelocEntry(RelocTypeLabelUse, (void*) m_pc);
-                this->EmitConst((uint32)opr1->AsLabelOpnd()->GetLabel(), 4);
+                AppendRelocEntry(RelocTypeLabelUse, (void*) m_pc, opr1->AsLabelOpnd()->GetLabel());
+                this->EmitConst(0, 4);
             }
             else
             {
@@ -917,24 +914,21 @@ modrm:
                 // Unconditional branch
                 AssertMsg(instr->IsBranchInstr(), "Invalid LABREL2 form");
 
-                AppendRelocEntry(RelocTypeBranch, (void*)m_pc);
-
-                // Save the target LabelInstr's address in the encoder buffer itself, using the 4-byte
-                // pcrel field of the branch instruction. This only works for long branches, obviously.
-                this->EmitConst((uint32)instr->AsBranchInstr()->GetTarget(), 4);
+                AppendRelocEntry(RelocTypeBranch, (void*)m_pc, instr->AsBranchInstr()->GetTarget());
+                this->EmitConst(0, 4);
             }
             else if (opr1->IsIntConstOpnd())
             {
-                AppendRelocEntry(RelocTypeCallPcrel, (void*)m_pc);
-                this->EmitConst(opr1->AsIntConstOpnd()->GetValue(), 4);
+                AppendRelocEntry(RelocTypeCallPcrel, (void*)m_pc, nullptr, (void*)opr1->AsIntConstOpnd()->GetValue());
+                this->EmitConst(0, 4);
                 AssertMsg( ( ((BYTE*)opr1->AsIntConstOpnd()->GetValue()) < m_encoder->m_encodeBuffer || ((BYTE *)opr1->AsIntConstOpnd()->GetValue()) >= m_encoder->m_encodeBuffer + m_encoder->m_encodeBufferSize), "Call Target within buffer.");
             }
             else if (opr1->IsHelperCallOpnd())
             {
-                AppendRelocEntry(RelocTypeCallPcrel, (void*)m_pc);
                 const void* fnAddress = IR::GetMethodAddress(opr1->AsHelperCallOpnd());
+                AppendRelocEntry(RelocTypeCallPcrel, (void*)m_pc, nullptr, fnAddress);
                 AssertMsg(sizeof(uint32) == sizeof(void*), "Sizes of void* assumed to be 32-bits");
-                this->EmitConst((uint32)fnAddress, 4);
+                this->EmitConst(0, 4);
                 AssertMsg( (((BYTE*)fnAddress) < m_encoder->m_encodeBuffer || ((BYTE *)fnAddress) >= m_encoder->m_encodeBuffer + m_encoder->m_encodeBufferSize), "Call Target within buffer.");
             }
             else
@@ -1280,13 +1274,13 @@ modrm:
 }
 
 int
-EncoderMD::AppendRelocEntry(RelocType type, void *ptr)
+EncoderMD::AppendRelocEntry(RelocType type, void *ptr, IR::LabelInstr* labelInstr, const void * fnAddress)
 {
     if (m_relocList == nullptr)
         m_relocList = Anew(m_encoder->m_tempAlloc, RelocList, m_encoder->m_tempAlloc);
 
     EncodeRelocAndLabels reloc;
-    reloc.init(type, ptr);
+    reloc.init(type, ptr, labelInstr, fnAddress);
 
     return m_relocList->Add(reloc);
 }
@@ -1340,7 +1334,7 @@ EncoderMD::FixRelocListEntry(uint32 index, int32 totalBytesSaved, BYTE *buffStar
                 uint32 count = field & 0xf;
 
                 AssertMsg(offset < (uint32)(buffEnd - buffStart), "Inlinee entry offset out of range");
-                *((uint32*) relocRecord.m_origPtr) = ((offset - totalBytesSaved) << 4) | count;
+                relocRecord.SetInlineOffset(((offset - totalBytesSaved) << 4) | count);
             }
             // adjust the ptr to the buffer itself
             relocRecord.m_ptr = (BYTE*) relocRecord.m_ptr - totalBytesSaved;
@@ -1395,7 +1389,7 @@ EncoderMD::FixMaps(uint32 brOffset, int32 bytesSaved, uint32 *inlineeFrameRecord
 /// before we copy the contents of the temporary buffer to the target buffer.
 ///----------------------------------------------------------------------------
 void
-EncoderMD::ApplyRelocs(uint32 codeBufferAddress)
+EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint * bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation)
 {
     for (int32 i = 0; i < m_relocList->Count(); i++)
     {
@@ -1408,7 +1402,13 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress)
         case RelocTypeCallPcrel:
             {
                 pcrel = (uint32)(codeBufferAddress + (BYTE*)reloc->m_ptr - m_encoder->m_encodeBuffer + 4);
-                *(uint32 *)relocAddress -= pcrel;
+                uint32 offset = (uint32)reloc->GetFnAddress() - pcrel;
+                if (!isFinalBufferValidation)
+                {
+                    Assert(*(uint32 *)relocAddress == 0);
+                    *(uint32 *)relocAddress = offset;
+                }
+                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeBranch:
@@ -1420,20 +1420,48 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress)
                     // short branch
                     pcrel = (uint32)(labelInstr->GetPC() - ((BYTE*)reloc->m_ptr + 1));
                     AssertMsg((int32)pcrel >= -128 && (int32)pcrel <= 127, "Offset doesn't fit in imm8.");
-                    *(BYTE*)relocAddress = (BYTE)pcrel;
+                    if (!isFinalBufferValidation)
+                    {
+                        Assert(*(BYTE*)relocAddress == 0);
+                        *(BYTE*)relocAddress = (BYTE)pcrel;
+                    }
+                    else
+                    {
+                        Encoder::EnsureRelocEntryIntegrity(codeBufferAddress, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(BYTE), (ptrdiff_t)labelInstr->GetPC() - ((ptrdiff_t)reloc->m_ptr + 1));
+                    }
                 }
                 else
                 {
                     pcrel = (uint32)(labelInstr->GetPC() - ((BYTE*)reloc->m_ptr + 4));
-                    *(uint32 *)relocAddress = pcrel;
+                    if (!isFinalBufferValidation)
+                    {
+                        Assert(*(uint32 *)relocAddress == 0);
+                        *(uint32 *)relocAddress = pcrel;
+                    }
+                    else
+                    {
+                        Encoder::EnsureRelocEntryIntegrity(codeBufferAddress, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(uint32), (ptrdiff_t)labelInstr->GetPC() - ((ptrdiff_t)reloc->m_ptr + 4));
+                    }
                 }
+                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, pcrel);
                 break;
             }
         case RelocTypeLabelUse:
             {
-                IR::LabelInstr * labelInstr = *(IR::LabelInstr**)relocAddress;
+                IR::LabelInstr * labelInstr = reloc->GetLabelInstrForRelocTypeLabelUse();
                 AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?");
-                *(uint32 *)relocAddress = (uint32)(labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress);
+                uint32 offset = uint32(labelInstr->GetPC() - m_encoder->m_encodeBuffer);
+                size_t targetAddress = (uint32)(offset + codeBufferAddress);
+                if (!isFinalBufferValidation)
+                {
+                    Assert(*(uint32 *)relocAddress == 0);
+                    *(uint32 *)relocAddress = targetAddress;
+                }
+                else
+                {
+                    Encoder::EnsureRelocEntryIntegrity(codeBufferAddress, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(size_t), targetAddress, false);
+                }
+                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeLabel:
@@ -1446,6 +1474,40 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress)
     }
 }
 
+uint
+EncoderMD::GetRelocDataSize(EncodeRelocAndLabels *reloc)
+{
+    switch (reloc->m_type)
+    {
+        case RelocTypeCallPcrel:
+        case RelocTypeLabelUse:
+        {
+            return sizeof(uint32);
+        }
+        case RelocTypeBranch:
+        {
+            if (reloc->isShortBr())
+            {
+                return sizeof(BYTE);
+            }
+            else
+            {
+                return sizeof(uint32);
+            }
+        }
+        default:
+        {
+            return 0;
+        }
+    }
+}
+
+BYTE * 
+EncoderMD::GetRelocBufferAddress(EncodeRelocAndLabels * reloc)
+{
+    return (BYTE*)reloc->m_ptr;
+}
+
 ///----------------------------------------------------------------------------
 ///
 /// EncodeRelocAndLabels::VerifyRelocList

+ 48 - 19
lib/Backend/i386/EncoderMD.h

@@ -31,15 +31,23 @@ private:
     union
     {
         IR::LabelInstr *    m_shortBrLabel;     // NULL if not a short branch
-        uint32              m_origInlineeOffset;
+        uint32              m_InlineeOffset;
         BYTE                m_nopCount;         // for AlignedLabel, how many nops do we need to be 16-byte aligned
     };
 
+    union
+    {
+        IR::LabelInstr       *    m_labelInstr;
+        const void           *    m_fnAddress;
+    };
+
 public:
-    void                init(RelocType type, void* ptr)
+    void                init(RelocType type, void* ptr, IR::LabelInstr* labelInstr, const void * fnAddress)
     {
         m_type = type;
         m_ptr = ptr;
+        m_InlineeOffset = 0;
+        m_labelInstr = nullptr;
 
         if (type == RelocTypeLabel)
         {
@@ -50,16 +58,21 @@ public:
         else
         {
             m_origPtr = ptr;
-            // in case we have to revert, we need to store original offset in code buffer
-            if (type == RelocTypeInlineeEntryOffset)
+            if (type == RelocTypeBranch)
             {
-                m_origInlineeOffset = *((uint32*)m_origPtr);
+                m_shortBrLabel = NULL;
+                m_labelInstr = labelInstr;
             }
-            else if (type == RelocTypeBranch)
+            else if (type == RelocTypeLabelUse)
             {
-                m_shortBrLabel = NULL;
+                Assert(labelInstr);
+                m_labelInstr = labelInstr;
+            }
+            else if (type == RelocTypeCallPcrel)
+            {
+                Assert(fnAddress);
+                m_fnAddress = fnAddress;
             }
-
         }
     }
 
@@ -76,12 +89,6 @@ public:
             return;
         }
 
-        // re-write original inlinee offset to code buffer
-        if (m_type == RelocTypeInlineeEntryOffset)
-        {
-            *((uint32*)m_origPtr) = m_origInlineeOffset;
-        }
-
         if (m_type == RelocTypeBranch)
         {
             m_shortBrLabel = NULL;
@@ -104,16 +111,27 @@ public:
     IR::LabelInstr *    getBrTargetLabel()  const
     {
         Assert(m_type == RelocTypeBranch);
-        return m_shortBrLabel == NULL ? *(IR::LabelInstr**)m_origPtr : m_shortBrLabel;
+        return m_shortBrLabel == NULL ? m_labelInstr : m_shortBrLabel;
     }
 
-
     IR::LabelInstr *    getLabel()  const
     {
         Assert(isLabel());
         return (IR::LabelInstr*) m_ptr;
     }
 
+    IR::LabelInstr * GetLabelInstrForRelocTypeLabelUse()
+    {
+        Assert(m_type == RelocTypeLabelUse && m_labelInstr);
+        return m_labelInstr;
+    }
+
+    const void * GetFnAddress()
+    {
+        Assert(m_type == RelocTypeCallPcrel && m_fnAddress);
+        return m_fnAddress;
+    }
+
     // get label original PC without shortening/alignment
     BYTE *  getLabelOrigPC()  const
     {
@@ -160,6 +178,16 @@ public:
             m_shortBrLabel->GetPC() - ((BYTE*)m_ptr + 1) >= -128 &&
             m_shortBrLabel->GetPC() - ((BYTE*)m_ptr + 1) <= 127;
     }
+
+    uint32 GetInlineOffset()
+    {
+        return m_InlineeOffset;
+    }
+
+    void SetInlineOffset(uint32 offset)
+    {
+        m_InlineeOffset = offset;
+    }
 };
 
 
@@ -181,8 +209,8 @@ public:
 
     ptrdiff_t       Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr);
     void            Init(Encoder *encoder);
-    void            ApplyRelocs(uint32 codeBufferAddress);
-
+    void            ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint * bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false);
+    uint            GetRelocDataSize(EncodeRelocAndLabels *reloc);
     void            EncodeInlineeCallInfo(IR::Instr *instr, uint32 offset);
     static bool     TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
     static bool     TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
@@ -190,7 +218,7 @@ public:
     static bool     UsesConditionCode(IR::Instr *instr);
     static bool     IsOPEQ(IR::Instr *instr);
     RelocList*      GetRelocList() const { return m_relocList; }
-    int             AppendRelocEntry(RelocType type, void *ptr);
+    int             AppendRelocEntry(RelocType type, void *ptr, IR::LabelInstr * labelInstr = nullptr, const void * fnAddress = nullptr);
     int             FixRelocListEntry(uint32 index, int32 totalBytesSaved, BYTE *buffStart, BYTE* buffEnd);
     void            FixMaps(uint32 brOffset, int32 bytesSaved, uint32 *inlineeFrameRecordsIndex, uint32 *inlineeFrameMapIndex,  uint32 *pragmaInstToRecordOffsetIndex, uint32 *offsetBuffIndex);
     void            UpdateRelocListWithNewBuffer(RelocList * relocList, BYTE * newBuffer, BYTE * oldBufferStart, BYTE * oldBufferEnd);
@@ -198,6 +226,7 @@ public:
     void            VerifyRelocList(BYTE *buffStart, BYTE *buffEnd);
 #endif
     void            AddLabelReloc(BYTE* relocAddress);
+    BYTE *          GetRelocBufferAddress(EncodeRelocAndLabels * reloc);
 
 private:
     const BYTE      GetOpcodeByte2(IR::Instr *instr);

+ 7 - 0
lib/Common/Core/SysInfo.cpp

@@ -241,6 +241,13 @@ AutoSystemInfo::SSE4_1Available() const
     return VirtualSseAvailable(4) && (CPUInfo[2] & (0x1 << 19));
 }
 
+BOOL
+AutoSystemInfo::SSE4_2Available() const
+{
+    Assert(initialized);
+    return VirtualSseAvailable(4) && (CPUInfo[2] & (0x1 << 20));
+}
+
 BOOL
 AutoSystemInfo::PopCntAvailable() const
 {

+ 1 - 0
lib/Common/Core/SysInfo.h

@@ -28,6 +28,7 @@ public:
 #if defined(_M_IX86) || defined(_M_X64)
     BOOL SSE3Available() const;
     BOOL SSE4_1Available() const;
+    BOOL SSE4_2Available() const;
     BOOL PopCntAvailable() const;
     BOOL LZCntAvailable() const;
     bool IsAtomPlatform() const;

+ 15 - 2
lib/Runtime/Language/JavascriptOperators.cpp

@@ -1400,10 +1400,23 @@ CommonNumber:
 
     Var JavascriptOperators::OP_LdCustomSpreadIteratorList(Var aRight, ScriptContext* scriptContext)
     {
+#if ENABLE_COPYONACCESS_ARRAY
+        // We know we're going to read from this array. Do the conversion before we try to perform checks on the head segment.
+        JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(aRight);
+#endif
         RecyclableObject* function = GetIteratorFunction(aRight, scriptContext);
         JavascriptMethod method = function->GetEntryPoint();
-        if (((JavascriptArray::Is(aRight) && method == JavascriptArray::EntryInfo::Values.GetOriginalEntryPoint())
-                || (TypedArrayBase::Is(aRight) && method == TypedArrayBase::EntryInfo::Values.GetOriginalEntryPoint()))
+        if (((JavascriptArray::Is(aRight) &&
+              (
+                  method == JavascriptArray::EntryInfo::Values.GetOriginalEntryPoint()
+                  // Verify that the head segment of the array covers all elements with no gaps.
+                  // Accessing an element on the prototype could have side-effects that would invalidate the optimization.
+                  && JavascriptArray::FromVar(aRight)->GetHead()->next == nullptr
+                  && JavascriptArray::FromVar(aRight)->GetHead()->left == 0
+                  && JavascriptArray::FromVar(aRight)->GetHead()->length == JavascriptArray::FromVar(aRight)->GetLength()
+                  && JavascriptArray::FromVar(aRight)->HasNoMissingValues()
+              )) ||
+             (TypedArrayBase::Is(aRight) && method == TypedArrayBase::EntryInfo::Values.GetOriginalEntryPoint()))
             // We can't optimize away the iterator if the array iterator prototype is user defined.
             && !JavascriptLibrary::ArrayIteratorPrototypeHasUserDefinedNext(scriptContext))
         {

+ 10 - 6
lib/Runtime/Library/JavascriptArray.cpp

@@ -3865,7 +3865,6 @@ namespace Js
         Throw::InternalError();
     }
 
-
     template <bool includesAlgorithm, typename T, typename P>
     Var JavascriptArray::TemplatedIndexOfHelper(T * pArr, Var search, P fromIndex, P toIndex, ScriptContext * scriptContext)
     {
@@ -3879,7 +3878,7 @@ namespace Js
         //Consider: enumerating instead of walking all indices
         for (P i = fromIndex; i < toIndex; i++)
         {
-            if (!TemplatedGetItem(pArr, i, &element, scriptContext))
+            if (!TryTemplatedGetItem(pArr, i, &element, scriptContext))
             {
                 if (doUndefinedSearch)
                 {
@@ -4300,7 +4299,8 @@ CaseDefault:
                     {
                         cs->Append(separator);
                     }
-                    if (TemplatedGetItem(arr, i, &item, scriptContext))
+
+                    if (TryTemplatedGetItem(arr, i, &item, scriptContext))
                     {
                         cs->Append(JavascriptArray::JoinToString(item, scriptContext));
                     }
@@ -4318,19 +4318,23 @@ CaseDefault:
 
                 JavascriptString *res = nullptr;
                 Var item;
+
                 if (TemplatedGetItem(arr, 0u, &item, scriptContext))
                 {
                     res = JavascriptArray::JoinToString(item, scriptContext);
                 }
-                if (TemplatedGetItem(arr, 1u, &item, scriptContext))
+
+                if (TryTemplatedGetItem(arr, 1u, &item, scriptContext))
                 {
                     JavascriptString *const itemString = JavascriptArray::JoinToString(item, scriptContext);
                     return res ? ConcatString::New(res, itemString) : itemString;
                 }
+
                 if(res)
                 {
                     return res;
                 }
+
                 goto Case0;
             }
 
@@ -4566,7 +4570,7 @@ Case0:
         {
             uint32 index = end - i;
 
-            if (!TemplatedGetItem(pArr, index, &element, scriptContext))
+            if (!TryTemplatedGetItem(pArr, index, &element, scriptContext))
             {
                 continue;
             }
@@ -9027,7 +9031,7 @@ Case0:
                         JavascriptNumber::ToVar(k, scriptContext),
                         obj);
 
-                    if (newArr)
+                    if (newArr && isBuiltinArrayCtor)
                     {
                         newArr->SetItem(k, mappedValue, PropertyOperation_None);
                     }

+ 9 - 2
lib/Runtime/Library/JavascriptArray.h

@@ -567,13 +567,20 @@ namespace Js
         // NativeArrays may change it's content type, but not others
         template <typename T> static bool MayChangeType() { return false; }
 
+        template<typename T, typename P>
+        static BOOL TryTemplatedGetItem(T *arr, P index, Var *element, ScriptContext *scriptContext)
+        {
+            return T::Is(arr) ? JavascriptArray::TemplatedGetItem(arr, index, element, scriptContext) :
+                JavascriptOperators::GetItem(arr, index, element, scriptContext);
+        }
+
         template <bool hasSideEffect, typename T, typename Fn>
         static void TemplatedForEachItemInRange(T * arr, uint32 startIndex, uint32 limitIndex, Var missingItem, ScriptContext * scriptContext, Fn fn)
         {
             for (uint32 i = startIndex; i < limitIndex; i++)
             {
                 Var element;
-                fn(i, TemplatedGetItem(arr, i, &element, scriptContext) ? element : missingItem);
+                fn(i, TryTemplatedGetItem(arr, i, &element, scriptContext) ? element : missingItem);
 
                 if (hasSideEffect && MayChangeType<T>() && !T::Is(arr))
                 {
@@ -590,7 +597,7 @@ namespace Js
             for (P i = startIndex; i < limitIndex; i++)
             {
                 Var element;
-                if (TemplatedGetItem(arr, i, &element, scriptContext))
+                if (TryTemplatedGetItem(arr, i, &element, scriptContext))
                 {
                     fn(i, element);
 

+ 3 - 3
lib/Runtime/Library/JavascriptExternalFunction.cpp

@@ -176,7 +176,7 @@ namespace Js
 
     Var JavascriptExternalFunction::ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
-        RUNTIME_ARGUMENTS(args, callInfo);
+        ARGUMENTS(args, callInfo);
         JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);
 
         ScriptContext * scriptContext = externalFunction->type->GetScriptContext();
@@ -260,7 +260,7 @@ namespace Js
 
     Var JavascriptExternalFunction::WrappedFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
-        RUNTIME_ARGUMENTS(args, callInfo);
+        ARGUMENTS(args, callInfo);
         JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);
         ScriptContext* scriptContext = externalFunction->type->GetScriptContext();
         Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException());
@@ -284,7 +284,7 @@ namespace Js
 
     Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
-        RUNTIME_ARGUMENTS(args, callInfo);
+        ARGUMENTS(args, callInfo);
         JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);
 
         externalFunction->PrepareExternalCall(&args);

+ 382 - 221
test/Array/Array_TypeConfusion_bugs.js

@@ -14,226 +14,387 @@ var tests = [
          name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements",
          body: function ()
          {
-             function test() {
-                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe];
-
-                 class MyArray extends Uint32Array { }
-                 Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
-
-                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
-                 var test = [float_val, float_val, float_val, float_val];
-                 test.length = 0x1000;
-                 test.__proto__ = new MyArray(0);
-
-                 var res = Array.prototype.slice.apply(test, []);  // OOB write
-                 assert.areEqual(0x1000, res.length, "res.length == 0x1000");
-                 assert.areEqual(float_val, res[0], "res[0] == float_val");
-                 assert.areEqual(float_val, res[1], "res[1] == float_val");
-                 assert.areEqual(float_val, res[2], "res[2] == float_val");
-                 assert.areEqual(float_val, res[3], "res[3] == float_val");
-                 assert.areEqual(undefined, res[4], "res[4] == float_val");
-                 assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7342689:OOB writes using type confusion in InternalFillFromPrototypes",
-         body: function ()
-         {
-             function test() {
-                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
-                 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe,
-                 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
-                 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
-
-                 class MyArray extends Uint32Array { }
-                 Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
-
-                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
-                 var test = [{}];
-                 delete test[0];
-                 test.length = 0x1000;
-                 var src = [float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
-                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
-                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
-                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
-                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val];
-                 test.__proto__ = src;
-                 test.__proto__.__proto__ = new MyArray(0);
-
-                //this will write 0xfffc0daddeadbabe to [arr1] + 0x1D8
-                 var res = Array.prototype.slice.apply(test, [])
-                 assert.areEqual(0x1000, res.length, "res.length == 0x1000");
-                 assert.areEqual(float_val, res[0], "res[0] == float_val");
-                 assert.areEqual(float_val, res[1], "res[1] == float_val");
-                 assert.areEqual(float_val, res[2], "res[2] == float_val");
-                 assert.areEqual(float_val, res[src.length-1], "res[src.length-1] == float_val");
-                 assert.areEqual(undefined, res[src.length], "res[src] == undefined");
-                 assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7307908:type confusion in Array.prototype.slice",
-         body: function ()
-         {
-             function test() {
-                 var arr = [1, 2]
-
-                //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper<unsigned int>
-                 Object.defineProperty(
-                     arr.constructor,
-                     Symbol.species,
-                     {
-                         value : function()
-                         {
-                            //change 'arr' from TypeIds_NativeIntArray to TypeIds_Array
-                             arr[0] = WScript;
-
-                            //return a TypeIds_NativeIntArray so we can read back out the 64 bit pointer as two 32bit ints.
-                             return [];
-                         }
-                     }
-                 );
-
-                //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer.
-                 var brr = arr.slice();
-
-                 assert.areEqual(2, brr.length, "brr.length == 2");
-                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
-                 assert.areEqual(2, brr[1], "brr[0] == WScript");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7342791:type confusion in Array.from",
-         body: function ()
-         {
-             function test() {
-                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
-
-                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
-                 var test = [float_val, float_val, float_val, float_val];
-                 delete test[0];
-                 delete test[1];
-                 delete test[2];
-
-                 var res = Array.from.apply(function(){return arr1}, [test]);
-                 assert.areEqual(4, res.length, "res.length == 4");
-                 assert.areEqual(undefined, res[0], "res[0] == undefined");
-                 assert.areEqual(undefined, res[1], "res[1] == undefined");
-                 assert.areEqual(undefined, res[2], "res[2] == undefined");
-                 assert.areEqual(float_val, res[3], "res[3] == float_val");
-
-                 assert.areEqual(['1','2','3'], Array.from.apply(()=>new Array(), ["123"]), "Array.from on iterable");
-                 assert.areEqual([1,2,3], Array.from.apply(()=>new Array(), [{"0":1, "1":2, "2":3, "length":3}]), "Array.from on non-iterable");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7342844:type confusion in Array.of",
-         body: function ()
-         {
-             function test() {
-                 var brr = Array.of.call(()=>[ 1, 2, 3, 4 ],
-                     WScript, // supply 2 copies of target so the brr array will have a length of 2 and we can read the 64bit pointer.
-                     WScript
-                 );
-
-                 assert.areEqual(2, brr.length, "brr.length == 2");
-                 assert.areEqual(WScript, brr[0], "res[0] == WScript");
-                 assert.areEqual(WScript, brr[1], "res[1] == WScript");
-                 assert.areEqual(undefined, brr[2], "res[2] == undefined");
-                 assert.areEqual(undefined, brr[3], "res[3] == undefined");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7342907:type confusion in Array.prototype.map",
-         body: function ()
-         {
-             function test() {
-                 var arr = [ 1, 2 ];
-
-                 Object.defineProperty(
-                     arr.constructor,
-                     Symbol.species,
-                     {
-                         value : function()
-                         {
-                             return [];
-                         }
-                     }
-                 );
-
-                //The value returned from our callback is directly set into the array whose type we create via the species.
-                 var brr = arr.map( function( v )
-                     {
-                         if( v == 1 )
-                             return WScript;
-                     }
-                 );
-
-                 assert.areEqual(2, brr.length, "brr.length == 2");
-                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
-                 assert.areEqual(undefined, brr[1], "brr[1] == undefined");
-             }
-             test();
-             test();
-             test();
-         }
-     },
-     {
-         name: "OS7342965:type confusion in Array.prototype.splice",
-         body: function ()
-         {
-             function test() {
-                //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values).
-                 var arr = [ WScript, WScript ];
-
-                //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice
-                 Object.defineProperty(
-                     arr.constructor,
-                     Symbol.species,
-                     {
-                         value : function()
-                         {
-                            //return a TypeIds_NativeIntArray so we can read back out a 64 bit pointer as two 32bit ints.
-                             return [ 1, 2, 3, 4 ];
-                         }
-                     }
-                 );
-
-                //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. The helper
-                //method ArraySegmentSpliceHelper<Var> will directly copy over the TypeIds_Array segment data
-                //into the TypeIds_NativeIntArray segment.
-                 var brr = arr.splice( 0, 2 );
-
-                 assert.areEqual(2, brr.length, "brr.length == 2");
-                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
-                 assert.areEqual(WScript, brr[1], "brr[1] == WScript");
-                 assert.areEqual(undefined, brr[2], "brr[2] == undefined");
-                 assert.areEqual(undefined, brr[3], "brr[3] == undefined");
-             }
-             test();
-             test();
-             test();
-         }
-     },
+            function test() {
+                var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                class MyArray extends Uint32Array { }
+                Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
+
+                var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                var test = [float_val, float_val, float_val, float_val];
+                test.length = 0x1000;
+                test.__proto__ = new MyArray(0);
+
+                var res = Array.prototype.slice.apply(test, []);  // OOB write
+                assert.areEqual(0x1000, res.length, "res.length == 0x1000");
+                assert.areEqual(float_val, res[0], "res[0] == float_val");
+                assert.areEqual(float_val, res[1], "res[1] == float_val");
+                assert.areEqual(float_val, res[2], "res[2] == float_val");
+                assert.areEqual(float_val, res[3], "res[3] == float_val");
+                assert.areEqual(undefined, res[4], "res[4] == float_val");
+                assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7342689:OOB writes using type confusion in InternalFillFromPrototypes",
+        body: function ()
+        {
+            function test() {
+                var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
+                0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe,
+                0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
+                0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                class MyArray extends Uint32Array { }
+                Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
+
+                var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                var test = [{}];
+                delete test[0];
+                test.length = 0x1000;
+                var src = [float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val];
+                test.__proto__ = src;
+                test.__proto__.__proto__ = new MyArray(0);
+
+               //this will write 0xfffc0daddeadbabe to [arr1] + 0x1D8
+                var res = Array.prototype.slice.apply(test, [])
+                assert.areEqual(0x1000, res.length, "res.length == 0x1000");
+                assert.areEqual(float_val, res[0], "res[0] == float_val");
+                assert.areEqual(float_val, res[1], "res[1] == float_val");
+                assert.areEqual(float_val, res[2], "res[2] == float_val");
+                assert.areEqual(float_val, res[src.length-1], "res[src.length-1] == float_val");
+                assert.areEqual(undefined, res[src.length], "res[src] == undefined");
+                assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7307908:type confusion in Array.prototype.slice",
+        body: function ()
+        {
+            function test() {
+                var arr = [1, 2]
+
+               //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper<unsigned int>
+                Object.defineProperty(
+                    arr.constructor,
+                    Symbol.species,
+                    {
+                        value : function()
+                        {
+                           //change 'arr' from TypeIds_NativeIntArray to TypeIds_Array
+                            arr[0] = WScript;
+
+                           //return a TypeIds_NativeIntArray so we can read back out the 64 bit pointer as two 32bit ints.
+                            return [];
+                        }
+                    }
+                );
+
+               //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer.
+                var brr = arr.slice();
+
+                assert.areEqual(2, brr.length, "brr.length == 2");
+                assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                assert.areEqual(2, brr[1], "brr[0] == WScript");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7342791:type confusion in Array.from",
+        body: function ()
+        {
+            function test() {
+                var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                var test = [float_val, float_val, float_val, float_val];
+                delete test[0];
+                delete test[1];
+                delete test[2];
+
+                var res = Array.from.apply(function(){return arr1}, [test]);
+                assert.areEqual(4, res.length, "res.length == 4");
+                assert.areEqual(undefined, res[0], "res[0] == undefined");
+                assert.areEqual(undefined, res[1], "res[1] == undefined");
+                assert.areEqual(undefined, res[2], "res[2] == undefined");
+                assert.areEqual(float_val, res[3], "res[3] == float_val");
+
+                assert.areEqual(['1','2','3'], Array.from.apply(()=>new Array(), ["123"]), "Array.from on iterable");
+                assert.areEqual([1,2,3], Array.from.apply(()=>new Array(), [{"0":1, "1":2, "2":3, "length":3}]), "Array.from on non-iterable");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7342844:type confusion in Array.of",
+        body: function ()
+        {
+            function test() {
+                var brr = Array.of.call(()=>[ 1, 2, 3, 4 ],
+                    WScript, // supply 2 copies of target so the brr array will have a length of 2 and we can read the 64bit pointer.
+                    WScript
+                );
+
+                assert.areEqual(2, brr.length, "brr.length == 2");
+                assert.areEqual(WScript, brr[0], "res[0] == WScript");
+                assert.areEqual(WScript, brr[1], "res[1] == WScript");
+                assert.areEqual(undefined, brr[2], "res[2] == undefined");
+                assert.areEqual(undefined, brr[3], "res[3] == undefined");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7342907:type confusion in Array.prototype.map",
+        body: function ()
+        {
+            function test() {
+                var arr = [ 1, 2 ];
+
+                Object.defineProperty(
+                    arr.constructor,
+                    Symbol.species,
+                    {
+                        value : function()
+                        {
+                            return [];
+                        }
+                    }
+                );
+
+               //The value returned from our callback is directly set into the array whose type we create via the species.
+                var brr = arr.map( function( v )
+                    {
+                        if( v == 1 )
+                            return WScript;
+                    }
+                );
+
+                assert.areEqual(2, brr.length, "brr.length == 2");
+                assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                assert.areEqual(undefined, brr[1], "brr[1] == undefined");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "type confusion in Array.prototype.map with Proxy",
+        body: function ()
+        {
+            function test() {
+                var d = [1,2,3];
+                class dummy {
+                    constructor() {
+                        return d;
+                    }
+                }
+
+                var handler = {
+                    get: function(target, name) {
+                        if(name == "length") {
+                            return 0x100;
+                        }
+
+                        return {[Symbol.species] : dummy};
+                    },
+
+                    has: function(target, name) {
+                        return true;
+                    }
+                };
+
+                var p = new Proxy([], handler);
+                var a = new Array(1,2,3);
+
+                function test(){
+                    return 0x777777777777;
+                }
+
+                var o = a.map.call(p, test);
+                assert.areEqual(Array(0x100).fill(0x777777777777), o);
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "OS7342965:type confusion in Array.prototype.splice",
+        body: function ()
+        {
+            function test() {
+               //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values).
+                var arr = [ WScript, WScript ];
+
+               //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice
+                Object.defineProperty(
+                    arr.constructor,
+                    Symbol.species,
+                    {
+                        value : function()
+                        {
+                           //return a TypeIds_NativeIntArray so we can read back out a 64 bit pointer as two 32bit ints.
+                            return [ 1, 2, 3, 4 ];
+                        }
+                    }
+                );
+
+               //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. The helper
+               //method ArraySegmentSpliceHelper<Var> will directly copy over the TypeIds_Array segment data
+               //into the TypeIds_NativeIntArray segment.
+                var brr = arr.splice( 0, 2 );
+
+                assert.areEqual(2, brr.length, "brr.length == 2");
+                assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                assert.areEqual(WScript, brr[1], "brr[1] == WScript");
+                assert.areEqual(undefined, brr[2], "brr[2] == undefined");
+                assert.areEqual(undefined, brr[3], "brr[3] == undefined");
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "type confusion in Array.prototype.join",
+        body: function ()
+        {
+            function test() {
+                var a = [0, 1, 2, 3];
+                var b = [];
+                delete a[0];
+                Object.setPrototypeOf(a, b)
+                Object.defineProperty(b, "0",
+                    {
+                        get: function() {
+                                a[2] = "abc";
+                                return -1;
+                            }
+                    });
+
+                assert.areEqual("-1,1,abc,3", a.join());
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "type confusion in Array.prototype.indexOf",
+        body: function ()
+        {
+            function test() {
+                var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                var a = [0, 1, 2, 3];
+                var b = [];
+                delete a[1];
+                Object.setPrototypeOf(a, b);
+                Object.defineProperty(b, "1",
+                    {
+                        get: function() {
+                                a[2] = float_val; //"abc";
+                                return -1;
+                            }
+                    });
+
+                assert.areEqual(3, a.indexOf(3));
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "type confusion in Array.prototype.lastIndexOf",
+        body: function ()
+        {
+            function test() {
+                var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                var a = [3, 2, 1, 0];
+                var b = [];
+                delete a[3];
+                Object.setPrototypeOf(a, b);
+                Object.defineProperty(b, "3",
+                    {
+                        get: function() {
+                                a[1] = float_val; //"abc";
+                                return -1;
+                            }
+                    });
+
+                assert.areEqual(0, a.lastIndexOf(3));
+            }
+            test();
+            test();
+            test();
+        }
+    },
+    {
+        name: "type confusion in Function.prototype.apply",
+        body: function ()
+        {
+            function test() {
+                var t = [1,2,3];
+
+                function f(){
+                    var h = [];
+                    var a = [...arguments]
+
+                    for(item in a){
+                        var n = new Number(a[item]);
+
+                        if( n < 0) {
+                            n = n + 0x100000000;
+                        }
+
+                        h.push(n.toString(16));
+                    }
+
+                    return h;
+                }
+
+                var q = f;
+
+                t.length = 20;
+                var o = {};
+                Object.defineProperty(o, '3', {
+                    get: function() {
+                        var ta = [];
+                        ta.fill.call(t, "natalie");
+                        return 5;
+                    }
+                });
+
+                t.__proto__ = o;
+
+                var j = [];
+                assert.areEqual("1,2,3,5,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN", f.apply(null, t).toString());
+            }
+            test();
+            test();
+            test();
+        }
+    },
 ];
 testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 21 - 0
test/es6/spread.js

@@ -231,6 +231,8 @@ var tests = [
       assert.throws(function() { eval('a(...x)--'); }, ReferenceError, "Spread with CallIPut throws a ReferenceError");
     }
   },
+/*
+    A fix for an unsafe optimization makes this portion of the test time out.
   {
     name: "BLUE 596934, 597412: Incorrect spread argument length handling",
     body: function () {
@@ -247,6 +249,25 @@ var tests = [
       assert.throws(function () { a(...new Array(3), ...new Array(1 << 32 - 2)); }, RangeError, "Total spread size greater than max call arg count throws RangeError");
     }
   },
+*/
+  {
+    name: "MSRC 34309: Guard against getter in prototype",
+    body: function () {
+        var x = [0x40];
+        x.length = 0x9;
+        
+        Object.defineProperty(Array.prototype, 1, {
+            get: function() {
+                x.length = 0;
+            }
+        });
+
+        var f = function(){
+            assert.areEqual(arguments.length, 2, "Changing length of x during spreading should truncate the spread.");
+        }
+        f(...x);
+    }
+  },
   {
     name: "BLUE 611774: Spread with a prefix operator is allowed anywhere",
     body: function () {