CVE-2015-3215: Improper Input Validation (CWE-20)
value: The NetKVM Windows Virtio driver allows remote attackers to cause a denial of service (guest crash) via a crafted length value in an IP packet, as demonstrated by a value that does not account for the size of the IP options.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 7.5
Published: 2017-06-26T15:29Z
Repository: https://github.com/YanVugenfirer/kvm-guest-drivers-windows
Language: C++
Files changed: NetKVM/Common/ParaNdis-Common.cpp, NetKVM/Common/ParaNdis-TX.cpp, NetKVM/Common/ndis56common.h, NetKVM/Common/sw-offload.cpp, NetKVM/wlh/ParaNdis6-Impl.cpp
Commit date: 2014-12-19 15:30:06 +1100
Commit message: NetKVM: BZ#1169718: Checking the length only on read

Signed-off-by: Joseph Hindin <yhindin@rehat.com>

Diff: diff --git a/NetKVM/Common/ParaNdis-Common.cpp b/NetKVM/Common/ParaNdis-Common.cpp
index 799fb91..9024ba6 100644
--- a/NetKVM/Common/ParaNdis-Common.cpp
+++ b/NetKVM/Common/ParaNdis-Common.cpp
@@ -2217,11 +2217,12 @@ void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext)
 tChecksumCheckResult ParaNdis_CheckRxChecksum(
                                             PARANDIS_ADAPTER *pContext,
                                             ULONG virtioFlags,
                                             tCompletePhysicalAddress *pPacketPages,
                                             ULONG ulPacketLength,
-                                            ULONG ulDataOffset)
+                                            ULONG ulDataOffset,
+                                            BOOLEAN verifyLength)
 {
     tOffloadSettingsFlags f = pContext->Offload.flags;
     tChecksumCheckResult res;
     tTcpIpPacketParsingResult ppr;
     ULONG flagsToCalculate = 0;
@@ -2245,11 +2246,12 @@ tChecksumCheckResult ParaNdis_CheckRxChecksum(
             if (f.fRxTCPv6Checksum) flagsToCalculate |= pcrTcpV6Checksum;
             if (f.fRxUDPv6Checksum) flagsToCalculate |= pcrUdpV6Checksum;
         }
     }
 
-    ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE, ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate, __FUNCTION__);
+    ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE, ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate,
+        verifyLength, __FUNCTION__);
 
     if (ppr.ipCheckSum == ppresIPTooShort || ppr.xxpStatus == ppresXxpIncomplete)
     {
         res.flags.IpOK = FALSE;
         res.flags.IpFailed = TRUE;
diff --git a/NetKVM/Common/ParaNdis-TX.cpp b/NetKVM/Common/ParaNdis-TX.cpp
index ad265bb..2820db2 100644
--- a/NetKVM/Common/ParaNdis-TX.cpp
+++ b/NetKVM/Common/ParaNdis-TX.cpp
@@ -647,10 +647,11 @@ void CNB::SetupLSO(virtio_net_hdr_basic *VirtioHeader, PVOID IpHeader, ULONG Eth
     PopulateIPLength(reinterpret_cast<IPv4Header*>(IpHeader), static_cast<USHORT>(EthPayloadLength));
 
     tTcpIpPacketParsingResult packetReview;
     packetReview = ParaNdis_CheckSumVerifyFlat(reinterpret_cast<IPv4Header*>(IpHeader), EthPayloadLength,
                                                pcrIpChecksum | pcrFixIPChecksum | pcrTcpChecksum | pcrFixPHChecksum,
+                                               FALSE,
                                                __FUNCTION__);
 
     if (packetReview.xxpCheckSum == ppresPCSOK || packetReview.fixedXxpCS)
     {
         auto IpHeaderOffset = m_Context->Offload.ipHeaderOffset;
@@ -668,11 +669,11 @@ void CNB::SetupLSO(virtio_net_hdr_basic *VirtioHeader, PVOID IpHeader, ULONG Eth
 
 USHORT CNB::QueryL4HeaderOffset(PVOID PacketData, ULONG IpHeaderOffset) const
 {
     USHORT Res;
     auto ppr = ParaNdis_ReviewIPPacket(RtlOffsetToPointer(PacketData, IpHeaderOffset),
-                                       GetDataLength(), __FUNCTION__);
+                                       GetDataLength(), FALSE, __FUNCTION__);
     if (ppr.ipStatus != ppresNotIP)
     {
         Res = static_cast<USHORT>(IpHeaderOffset + ppr.ipHeaderSize);
     }
     else
@@ -694,11 +695,11 @@ void CNB::SetupCSO(virtio_net_hdr_basic *VirtioHeader, ULONG L4HeaderOffset) con
 
 void CNB::DoIPHdrCSO(PVOID IpHeader, ULONG EthPayloadLength) const
 {
     ParaNdis_CheckSumVerifyFlat(IpHeader,
                                 EthPayloadLength,
-                                pcrIpChecksum | pcrFixIPChecksum,
+                                pcrIpChecksum | pcrFixIPChecksum, FALSE,
                                 __FUNCTION__);
 }
 
 bool CNB::FillDescriptorSGList(CTXDescriptor &Descriptor, ULONG ParsedHeadersLength) const
 {
diff --git a/NetKVM/Common/ndis56common.h b/NetKVM/Common/ndis56common.h
index e660d82..e61e021 100644
--- a/NetKVM/Common/ndis56common.h
+++ b/NetKVM/Common/ndis56common.h
@@ -742,11 +742,12 @@ void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsF
 tChecksumCheckResult ParaNdis_CheckRxChecksum(
                                             PARANDIS_ADAPTER *pContext,
                                             ULONG virtioFlags,
                                             tCompletePhysicalAddress *pPacketPages,
                                             ULONG ulPacketLength,
-                                            ULONG ulDataOffset);
+                                            ULONG ulDataOffset,
+                                            BOOLEAN verifyLength);
 
 void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext);
 
 /*****************************************************
 Procedures to implement for NDIS specific implementation
@@ -915,26 +916,28 @@ typedef enum _tagPacketOffloadRequest
 tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(
                                                 tCompletePhysicalAddress *pDataPages,
                                                 ULONG ulDataLength,
                                                 ULONG ulStartOffset,
                                                 ULONG flags,
+                                                BOOLEAN verifyLength,
                                                 LPCSTR caller);
 
 static __inline
 tTcpIpPacketParsingResult ParaNdis_CheckSumVerifyFlat(
                                                 PVOID pBuffer,
                                                 ULONG ulDataLength,
                                                 ULONG flags,
+                                                BOOLEAN verifyLength,
                                                 LPCSTR caller)
 {
     tCompletePhysicalAddress SGBuffer;
     SGBuffer.Virtual = pBuffer;
     SGBuffer.size = ulDataLength;
-    return ParaNdis_CheckSumVerify(&SGBuffer, ulDataLength, 0, flags, caller);
+    return ParaNdis_CheckSumVerify(&SGBuffer, ulDataLength, 0, flags, verifyLength, caller);
 }
 
-tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller);
+tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, BOOLEAN verityLength, LPCSTR caller);
 
 BOOLEAN ParaNdis_AnalyzeReceivedPacket(PVOID headersBuffer, ULONG dataLength, PNET_PACKET_INFO packetInfo);
 ULONG ParaNdis_StripVlanHeaderMoveHead(PNET_PACKET_INFO packetInfo);
 VOID ParaNdis_PadPacketToMinimalLength(PNET_PACKET_INFO packetInfo);
 BOOLEAN ParaNdis_IsSendPossible(PARANDIS_ADAPTER *pContext);
diff --git a/NetKVM/Common/sw-offload.cpp b/NetKVM/Common/sw-offload.cpp
index 2be3d80..f212945 100644
--- a/NetKVM/Common/sw-offload.cpp
+++ b/NetKVM/Common/sw-offload.cpp
@@ -198,11 +198,11 @@ ProcessUDPHeader(tTcpIpPacketParsingResult _res, PVOID pIpHeader, ULONG len, USH
     }
     return res;
 }
 
 static __inline tTcpIpPacketParsingResult
-QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
+QualifyIpPacket(IPHeader *pIpHeader, ULONG len, BOOLEAN verifyLength)
 {
     tTcpIpPacketParsingResult res;
     res.value = 0;
 
     if (len < 4)
@@ -233,14 +233,14 @@ QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
         if (res.ipStatus == ppresNotIP)
         {
             return res;
         }
 
-        if (ipHeaderSize >= fullLength || len < fullLength)
+        if (ipHeaderSize >= fullLength || ( verifyLength && len < fullLength))
         {
-            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n", __FUNCTION__,
-                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));
+            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d, verify = %s\n", __FUNCTION__,
+                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len, (verifyLength ? "true" : "false")));
             res.ipCheckSum = ppresIPTooShort;
             return res;
         }
     }
     else if (ip_version == 6)
@@ -256,11 +256,12 @@ QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
         ipHeaderSize = sizeof(pIpHeader->v6);
         res.ipStatus = ppresIPV6;
         res.ipCheckSum = ppresCSOK;
         fullLength = swap_short(pIpHeader->v6.ip6_payload_len);
         fullLength += ipHeaderSize;
-        if (len < fullLength)
+
+        if (verifyLength && (len < fullLength))
         {
             res.ipStatus = ppresNotIP;
             return res;
         }
         while (nextHeader != 59)
@@ -646,15 +647,16 @@ static __inline VOID PrintOutParsingResult(
 tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(
                                                 tCompletePhysicalAddress *pDataPages,
                                                 ULONG ulDataLength,
                                                 ULONG ulStartOffset,
                                                 ULONG flags,
+                                                BOOLEAN verifyLength,
                                                 LPCSTR caller)
 {
     IPHeader *pIpHeader = (IPHeader *) RtlOffsetToPointer(pDataPages[0].Virtual, ulStartOffset);
 
-    tTcpIpPacketParsingResult res = QualifyIpPacket(pIpHeader, ulDataLength);
+    tTcpIpPacketParsingResult res = QualifyIpPacket(pIpHeader, ulDataLength, verifyLength);
     if (res.ipStatus == ppresNotIP || res.ipCheckSum == ppresIPTooShort)
         return res;
 
     if (res.ipStatus == ppresIPV4)
     {
@@ -700,13 +702,13 @@ tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(
     }
     PrintOutParsingResult(res, 1, caller);
     return res;
 }
 
-tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller)
+tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, BOOLEAN verifyLength, LPCSTR caller)
 {
-    tTcpIpPacketParsingResult res = QualifyIpPacket((IPHeader *) buffer, size);
+    tTcpIpPacketParsingResult res = QualifyIpPacket((IPHeader *) buffer, size, verifyLength);
     PrintOutParsingResult(res, 1, caller);
     return res;
 }
 
 static __inline
diff --git a/NetKVM/wlh/ParaNdis6-Impl.cpp b/NetKVM/wlh/ParaNdis6-Impl.cpp
index 8b207d5..f7cfd62 100644
--- a/NetKVM/wlh/ParaNdis6-Impl.cpp
+++ b/NetKVM/wlh/ParaNdis6-Impl.cpp
@@ -864,11 +864,11 @@ tPacketIndicationType ParaNdis_PrepareReceivedPacket(
                 csRes = ParaNdis_CheckRxChecksum(
                     pContext,
                     pHeader->flags,
                     &pBuffersDesc->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE],
                     pPacketInfo->dataLength,
-                    nBytesStripped);
+                    nBytesStripped, TRUE);
                 if (csRes.value)
                 {
                     NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO qCSInfo;
                     qCSInfo.Value = NULL;
                     qCSInfo.Receive.IpChecksumFailed = csRes.flags.IpFailed;

Vulnerable code:                                             ULONG ulDataOffset)
    ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE, ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate, __FUNCTION__);
                                       GetDataLength(), __FUNCTION__);
                                pcrIpChecksum | pcrFixIPChecksum,
                                            ULONG ulDataOffset);
    return ParaNdis_CheckSumVerify(&SGBuffer, ulDataLength, 0, flags, caller);
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller);
QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
        if (ipHeaderSize >= fullLength || len < fullLength)
            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n", __FUNCTION__,
                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));
        if (len < fullLength)
    tTcpIpPacketParsingResult res = QualifyIpPacket(pIpHeader, ulDataLength);
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, LPCSTR caller)
    tTcpIpPacketParsingResult res = QualifyIpPacket((IPHeader *) buffer, size);
                    nBytesStripped);
Fixed code:                                             ULONG ulDataOffset,
                                            BOOLEAN verifyLength)
    ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE, ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate,
        verifyLength, __FUNCTION__);
                                               FALSE,
                                       GetDataLength(), FALSE, __FUNCTION__);
                                pcrIpChecksum | pcrFixIPChecksum, FALSE,
                                            ULONG ulDataOffset,
                                            BOOLEAN verifyLength);
                                                BOOLEAN verifyLength,
                                                BOOLEAN verifyLength,
    return ParaNdis_CheckSumVerify(&SGBuffer, ulDataLength, 0, flags, verifyLength, caller);
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, BOOLEAN verityLength, LPCSTR caller);
QualifyIpPacket(IPHeader *pIpHeader, ULONG len, BOOLEAN verifyLength)
        if (ipHeaderSize >= fullLength || ( verifyLength && len < fullLength))
            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d, verify = %s\n", __FUNCTION__,
                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len, (verifyLength ? "true" : "false")));

        if (verifyLength && (len < fullLength))
                                                BOOLEAN verifyLength,
    tTcpIpPacketParsingResult res = QualifyIpPacket(pIpHeader, ulDataLength, verifyLength);
tTcpIpPacketParsingResult ParaNdis_ReviewIPPacket(PVOID buffer, ULONG size, BOOLEAN verifyLength, LPCSTR caller)
    tTcpIpPacketParsingResult res = QualifyIpPacket((IPHeader *) buffer, size, verifyLength);
                    nBytesStripped, TRUE);