Kaynağa Gözat

When deserializing the parser state cache, verify the script code matches the script used to create the cache

Calculate a checksum of the script code and store it in the stream along with the parser state cache. When we try and use this parser state cache later, make sure the script we are given back matches the script we used to create the cache. If it doesn't for some reason, discard the parser state cache as it may not match the script code exactly.

Fixes:
https://microsoft.visualstudio.com/OS/_workitems/edit/17542547
https://microsoft.visualstudio.com/OS/_workitems/edit/17713429
Taylor Woll 7 yıl önce
ebeveyn
işleme
d1b9e0ebc5

+ 0 - 28
lib/Backend/Encoder.cpp

@@ -997,34 +997,6 @@ void Encoder::EnsureRelocEntryIntegrity(size_t newBufferStartAddress, size_t cod
     }
 }
 
-uint Encoder::CalculateCRC(uint bufferCRC, size_t data)
-{
-#if defined(_WIN32) || defined(__SSE4_2__)
-#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
-#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;

+ 0 - 2
lib/Backend/Encoder.h

@@ -63,8 +63,6 @@ private:
     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);

+ 3 - 2
lib/Backend/amd64/EncoderMD.cpp

@@ -4,6 +4,7 @@
 //-------------------------------------------------------------------------------------------------------
 
 #include "Backend.h"
+#include "Core/CRC.h"
 
 #include "X64Encode.h"
 
@@ -1637,7 +1638,7 @@ EncoderMD::ApplyRelocs(size_t codeBufferAddress_, size_t codeSize, uint * buffer
                     }
                 }
 
-                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, pcrel);
+                *bufferCRC = CalculateCRC(*bufferCRC, pcrel);
 
                 break;
             }
@@ -1658,7 +1659,7 @@ EncoderMD::ApplyRelocs(size_t codeBufferAddress_, size_t codeSize, uint * buffer
                 {
                     Encoder::EnsureRelocEntryIntegrity(codeBufferAddress_, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(size_t), targetAddress, false);
                 }
-                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
+                *bufferCRC = CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeLabel:

+ 4 - 3
lib/Backend/i386/EncoderMD.cpp

@@ -4,6 +4,7 @@
 //-------------------------------------------------------------------------------------------------------
 
 #include "Backend.h"
+#include "Core/CRC.h"
 
 #include "X86Encode.h"
 
@@ -1431,7 +1432,7 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint * bufferC
                     Assert(*(uint32 *)relocAddress == 0);
                     *(uint32 *)relocAddress = offset;
                 }
-                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
+                *bufferCRC = CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeBranch:
@@ -1466,7 +1467,7 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint * bufferC
                         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);
+                *bufferCRC = CalculateCRC(*bufferCRC, pcrel);
                 break;
             }
         case RelocTypeLabelUse:
@@ -1484,7 +1485,7 @@ EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint * bufferC
                 {
                     Encoder::EnsureRelocEntryIntegrity(codeBufferAddress, codeSize, (size_t)m_encoder->m_encodeBuffer, (size_t)relocAddress, sizeof(size_t), targetAddress, false);
                 }
-                *bufferCRC = Encoder::CalculateCRC(*bufferCRC, offset);
+                *bufferCRC = CalculateCRC(*bufferCRC, offset);
                 break;
             }
         case RelocTypeLabel:

+ 28 - 0
lib/Common/Core/CRC.cpp

@@ -25,3 +25,31 @@ unsigned int CalculateCRC32(const char* in)
     }
     return crc ^ (unsigned int)-1;
 }
+
+uint CalculateCRC(uint bufferCRC, size_t data)
+{
+#if defined(_WIN32) || defined(__SSE4_2__)
+#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
+#endif
+    return CalculateCRC32(bufferCRC, data);
+}
+
+uint 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;
+}

+ 4 - 0
lib/Common/Core/CRC.h

@@ -51,3 +51,7 @@ static const unsigned int crc_32_tab[] =
 unsigned int CalculateCRC32(unsigned int bufferCRC, size_t data);
 
 unsigned int CalculateCRC32(const char* in);
+
+uint CalculateCRC(uint bufferCRC, size_t data);
+
+uint CalculateCRC(uint bufferCRC, size_t count, _In_reads_bytes_(count) void * buffer);

+ 39 - 5
lib/Runtime/Base/ScriptContext.cpp

@@ -39,6 +39,7 @@
 
 #include "ByteCode/ByteCodeSerializer.h"
 #include "Language/SimpleDataCacheWrapper.h"
+#include "Core/CRC.h"
 
 namespace Js
 {
@@ -2119,6 +2120,7 @@ namespace Js
 
     HRESULT ScriptContext::TryDeserializeParserState(
         _In_ ULONG grfscr,
+        _In_ uint sourceCRC,
         _In_ charcount_t cchLength,
         _In_ SRCINFO *srcInfo,
         _In_ Js::Utf8SourceInfo* utf8SourceInfo,
@@ -2143,18 +2145,37 @@ namespace Js
 
 #ifdef ENABLE_WININET_PROFILE_DATA_CACHE
         // Find the parser state block in the read stream and get the size of the block in bytes.
-        ULONG byteCount = 0;
+        ULONG blockByteCount = 0;
         DebugOnly(auto url = !srcInfo->sourceContextInfo->isHostDynamicDocument ? srcInfo->sourceContextInfo->url : this->GetUrl());
 
         OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Trying to read parser state cache for '%s'\n"), url);
 
-        hr = pDataCache->SeekReadStreamToBlock(SimpleDataCacheWrapper::BlockType_ParserState, &byteCount);
+        hr = pDataCache->SeekReadStreamToBlock(SimpleDataCacheWrapper::BlockType_ParserState, &blockByteCount);
         if (FAILED(hr))
         {
             OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to find parser state cache in the stream (hr = 0x%08lx) for '%s'\n"), hr, url);
             return hr;
         }
 
+        uint expectedCRC = 0;
+        hr = pDataCache->Read(&expectedCRC);
+        if (FAILED(hr))
+        {
+            OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to read CRC value (hr = 0x%08lx) for '%s'\n"), hr, url);
+            return hr;
+        }
+
+        OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Computed CRC value = 0x%08lx (expected CRC value = 0x%08lx) for '%s'\n"), sourceCRC, expectedCRC, url);
+
+        if (expectedCRC != sourceCRC)
+        {
+            OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Fail CRC check, discarding parser state cache for '%s'\n"), url);
+            return E_FAIL;
+        }
+
+        // The block includes a 4-byte CRC before the parser state cache.
+        ULONG byteCount = blockByteCount - sizeof(uint);
+
         // The contract for this bytecode buffer is that it is available as long as we have this ScriptContext.
         // We will use this buffer as the string table needed to back the deferred stubs as well as bytecode
         // for defer deserialized functions.
@@ -2213,6 +2234,7 @@ namespace Js
     }
 
     HRESULT ScriptContext::TrySerializeParserState(
+        _In_ uint sourceCRC,
         _In_ LPCUTF8 pszSrc,
         _In_ size_t cbLength,
         _In_ SRCINFO *srcInfo,
@@ -2263,7 +2285,7 @@ namespace Js
 
         OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Trying to write parser state cache (%lu bytes) to stream for '%s'\n"), serializeParserStateCacheSize, url);
 
-        hr = pDataCache->StartBlock(Js::SimpleDataCacheWrapper::BlockType_ParserState, serializeParserStateCacheSize);
+        hr = pDataCache->StartBlock(Js::SimpleDataCacheWrapper::BlockType_ParserState, serializeParserStateCacheSize + sizeof(uint));
 
         if (FAILED(hr))
         {
@@ -2271,6 +2293,16 @@ namespace Js
             return hr;
         }
 
+        hr = pDataCache->Write(sourceCRC);
+
+        OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Computed CRC value = 0x%08lx for '%s'\n"), sourceCRC, url);
+
+        if (FAILED(hr))
+        {
+            OUTPUT_TRACE_DEBUGONLY(Js::DataCachePhase, _u(" Failed to write CRC data to the data stream (hr = 0x%08lx) for '%s'\n"), hr, url);
+            return hr;
+        }
+
         hr = pDataCache->WriteArray(serializeParserStateCacheBuffer, serializeParserStateCacheSize);
 
         if (FAILED(hr))
@@ -2314,10 +2346,12 @@ namespace Js
             && !this->IsScriptContextInDebugMode();
         byte* parserStateCacheBuffer = nullptr;
         DWORD parserStateCacheByteCount = 0;
+        uint computedSourceCRC = 0;
 
         if (fUseParserStateCache)
         {
-            hr = TryDeserializeParserState(grfscr, cchLength, srcInfo, utf8SourceInfo, sourceIndex, isCesu8, nullptr, func, &parserStateCacheBuffer, &parserStateCacheByteCount, pDataCache);
+            computedSourceCRC = CalculateCRC(0, cbLength, (void*)pszSrc);
+            hr = TryDeserializeParserState(grfscr, computedSourceCRC, cchLength, srcInfo, utf8SourceInfo, sourceIndex, isCesu8, nullptr, func, &parserStateCacheBuffer, &parserStateCacheByteCount, pDataCache);
 #ifdef ENABLE_WININET_PROFILE_DATA_CACHE
             // ERROR_WRITE_PROTECT indicates we cannot cache this script for whatever reason.
             // Disable generating and serializing the parser state cache.
@@ -2366,7 +2400,7 @@ namespace Js
             if (fUseParserStateCache)
             {
                 Assert(*func != nullptr);
-                TrySerializeParserState(pszSrc, cbLength, srcInfo, *func, parserStateCacheBuffer, parserStateCacheByteCount, pDataCache);
+                TrySerializeParserState(computedSourceCRC, pszSrc, cbLength, srcInfo, *func, parserStateCacheBuffer, parserStateCacheByteCount, pDataCache);
             }
         }
 #ifdef ENABLE_SCRIPT_DEBUGGING

+ 2 - 0
lib/Runtime/Base/ScriptContext.h

@@ -1291,6 +1291,7 @@ private:
 
         HRESULT TryDeserializeParserState(
             _In_ ULONG grfscr,
+            _In_ uint sourceCRC,
             _In_ charcount_t cchLength,
             _In_ SRCINFO *srcInfo,
             _In_ Js::Utf8SourceInfo* utf8SourceInfo,
@@ -1303,6 +1304,7 @@ private:
             _In_ Js::SimpleDataCacheWrapper* pDataCache);
 
         HRESULT TrySerializeParserState(
+            _In_ uint sourceCRC,
             _In_ LPCUTF8 pszSrc,
             _In_ size_t cbLength,
             _In_ SRCINFO *srcInfo,