VYPR
High severity7.5NVD Advisory· Published Oct 14, 2016· Updated May 6, 2026

CVE-2016-3382

CVE-2016-3382

Description

The scripting engines in Microsoft Internet Explorer 9 through 11 and Microsoft Edge allow remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted web site, as demonstrated by the Chakra JavaScript engine, aka "Scripting Engine Memory Corruption Vulnerability."

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Microsoft.ChakraCoreNuGet
< 1.2.11.2.1

Patches

1
f05c42e64c3b

Changes addressing CVE_2016-3382, CVE-2016-3385, CVE-2016-3386, CVE-2016-3389,

https://github.com/chakra-core/ChakraCorePaul LeathersSep 8, 2016via ghsa
24 files changed · +993 322
  • jenkins/check_copyright.sh+1 0 modified
    @@ -33,6 +33,7 @@ git diff --name-only `git merge-base origin/master HEAD` HEAD |
         grep -v -E '\.def$' |
         grep -v -E '\.inc$' |
         grep -v -E 'test/benchmarks/.*\.js$' |
    +    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
    
  • lib/Backend/amd64/EncoderMD.cpp+72 9 modified
    @@ -506,8 +506,8 @@ EncoderMD::EmitImmed(IR::Opnd * opnd, int opSize, int sbit, bool allow64Immediat
             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)
     {
     
         for (int32 i = 0; i < m_relocList->Count(); i++)
    @@ -1561,22 +1561,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:
    @@ -1589,6 +1619,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
     ///----------------------------------------------------------------------------
     ///
    
  • lib/Backend/amd64/EncoderMD.h+24 14 modified
    @@ -33,7 +33,7 @@ class EncodeRelocAndLabels
         {
             IR::LabelInstr* m_labelInstr;        // ptr to Br Label
             BYTE            m_nopCount;
    -        uint64          m_origInlineeOffset;
    +        uint64          m_InlineeOffset;
         };
         bool                m_isShortBr;
     
    @@ -42,6 +42,9 @@ class EncodeRelocAndLabels
         {
             m_type = type;
             m_ptr = ptr;
    +        m_InlineeOffset = 0;
    +        m_isShortBr = false;
    +
             if (type == RelocTypeLabel)
             {
                 // preserve original PC for labels
    @@ -51,16 +54,17 @@ class EncodeRelocAndLabels
             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 @@ class EncodeRelocAndLabels
                 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 @@ class EncodeRelocAndLabels
     
         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 @@ class EncodeRelocAndLabels
                 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 @@ class EncoderMD
         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);
    
  • lib/Backend/arm64/EncoderMD.h+1 1 modified
    @@ -53,7 +53,7 @@ class EncoderMD
         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; }
    
  • lib/Backend/arm/EncoderMD.cpp+1 1 modified
    @@ -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)
         {
    
  • lib/Backend/arm/EncoderMD.h+1 1 modified
    @@ -55,7 +55,7 @@ class EncoderMD
         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);
    
  • lib/Backend/Chakra.Backend.vcxproj+6 1 modified
    @@ -409,10 +409,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>
    \ No newline at end of file
    
  • lib/Backend/Chakra.Backend.vcxproj.filters+1 0 modified
    @@ -299,6 +299,7 @@
         </ClInclude>
         <ClInclude Include="IRBaseTypeList.h" />
         <ClInclude Include="SwitchIRBuilder.h" />
    +    <ClInclude Include="CRC.h" />
       </ItemGroup>
       <ItemGroup>
         <MASM Include="$(MSBuildThisFileDirectory)amd64\LinearScanMdA.asm">
    
  • lib/Backend/CRC.h+51 0 added
    @@ -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);
    +}
    \ No newline at end of file
    
  • lib/Backend/Encoder.cpp+231 10 modified
    @@ -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;
    @@ -791,6 +968,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.
    @@ -849,17 +1028,40 @@ 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;
     
                 // fix the BR
                 // 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)
             {
    @@ -870,7 +1072,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))
    @@ -882,17 +1086,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;
    @@ -905,13 +1120,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;
     }
    
  • lib/Backend/Encoder.h+10 2 modified
    @@ -49,15 +49,23 @@ class Encoder
     #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
     };
     
    
  • lib/Backend/GlobOpt.cpp+2 1 modified
    @@ -926,9 +926,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;
    
  • lib/Backend/i386/EncoderMD.cpp+90 28 modified
    @@ -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 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
                     {
                         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
                 {
    @@ -918,24 +915,21 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
                     // 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
    @@ -1281,13 +1275,13 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress)
     }
     
     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);
     }
    @@ -1341,7 +1335,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;
    @@ -1396,7 +1390,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++)
         {
    @@ -1409,7 +1403,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:
    @@ -1421,20 +1421,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:
    @@ -1447,6 +1475,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
    
  • lib/Backend/i386/EncoderMD.h+48 19 modified
    @@ -31,15 +31,23 @@ class EncodeRelocAndLabels
         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 @@ class EncodeRelocAndLabels
             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 @@ class EncodeRelocAndLabels
                 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 @@ class EncodeRelocAndLabels
         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 @@ class EncodeRelocAndLabels
                 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,23 +209,24 @@ class EncoderMD
     
         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);
         static bool     SetsConditionCode(IR::Instr *instr);
         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);
     #ifdef DBG
         void            VerifyRelocList(BYTE *buffStart, BYTE *buffEnd);
     #endif
         void            AddLabelReloc(BYTE* relocAddress);
    +    BYTE *          GetRelocBufferAddress(EncodeRelocAndLabels * reloc);
     
     private:
         const BYTE      GetOpcodeByte2(IR::Instr *instr);
    
  • lib/Backend/LowerMDShared.cpp+1 1 modified
    @@ -8871,7 +8871,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)
    
  • lib/Common/Core/SysInfo.cpp+7 0 modified
    @@ -197,6 +197,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
     {
    
  • lib/Common/Core/SysInfo.h+1 0 modified
    @@ -28,6 +28,7 @@ class AutoSystemInfo : public SYSTEM_INFO
     #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;
    
  • lib/Runtime/Language/JavascriptOperators.cpp+15 2 modified
    @@ -1362,10 +1362,23 @@ namespace Js
     
         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))
             {
    
  • lib/Runtime/Library/JavascriptArray.cpp+10 6 modified
    @@ -3857,7 +3857,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)
         {
    @@ -3871,7 +3870,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)
                     {
    @@ -4236,7 +4235,8 @@ namespace Js
                         {
                             cs->Append(separator);
                         }
    -                    if (TemplatedGetItem(arr, i, &item, scriptContext))
    +
    +                    if (TryTemplatedGetItem(arr, i, &item, scriptContext))
                         {
                             cs->Append(JavascriptArray::JoinToString(item, scriptContext));
                         }
    @@ -4254,19 +4254,23 @@ namespace Js
     
                     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;
                 }
     
    @@ -4503,7 +4507,7 @@ namespace Js
             {
                 uint32 index = end - i;
     
    -            if (!TemplatedGetItem(pArr, index, &element, scriptContext))
    +            if (!TryTemplatedGetItem(pArr, index, &element, scriptContext))
                 {
                     continue;
                 }
    @@ -9017,7 +9021,7 @@ namespace Js
                             JavascriptNumber::ToVar(k, scriptContext),
                             obj);
     
    -                    if (newArr)
    +                    if (newArr && isBuiltinArrayCtor)
                         {
                             newArr->SetItem(k, mappedValue, PropertyOperation_None);
                         }
    
  • lib/Runtime/Library/JavascriptArray.h+9 2 modified
    @@ -567,13 +567,20 @@ namespace Js
             template <> static bool MayChangeType<JavascriptNativeIntArray>() { return true; }
             template <> static bool MayChangeType<JavascriptNativeFloatArray>() { return true; }
     
    +        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);
     
    
  • lib/Runtime/Library/JavascriptExternalFunction.cpp+3 3 modified
    @@ -250,7 +250,7 @@ namespace Js
     
         Var JavascriptExternalFunction::ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
         {
    -        RUNTIME_ARGUMENTS(args, callInfo);
    +        ARGUMENTS(args, callInfo);
             JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);
     
             // Deferred constructors which are not callable fall back to using the RecyclableObject::DefaultEntryPoint. In order to call
    @@ -281,7 +281,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());
    @@ -298,7 +298,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);
    
  • test/Array/Array_TypeConfusion_bugs.js+382 221 modified
    @@ -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" });
    
  • test/es6/spread.js+21 0 modified
    @@ -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 () {
    
    
  • THIRD-PARTY-NOTICES.txt+5 0 modified
    @@ -199,6 +199,11 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
     
    +---------------------------------------------
     
    +CRC.h
    +
    +COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or code or tables 
    +extracted from it, as desired without restriction. 
     
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.