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/sw-offload.cpp
Commit date: 2014-12-19 15:29:52 +1100
Commit message: NetKVM: BZ#1169718: More rigoruous testing of incoming packet

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

Diff: diff --git a/NetKVM/Common/ParaNdis-Common.cpp b/NetKVM/Common/ParaNdis-Common.cpp
index 704a2d5..799fb91 100644
--- a/NetKVM/Common/ParaNdis-Common.cpp
+++ b/NetKVM/Common/ParaNdis-Common.cpp
@@ -2220,15 +2220,14 @@ tChecksumCheckResult ParaNdis_CheckRxChecksum(
                                             tCompletePhysicalAddress *pPacketPages,
                                             ULONG ulPacketLength,
                                             ULONG ulDataOffset)
 {
     tOffloadSettingsFlags f = pContext->Offload.flags;
-    tChecksumCheckResult res, resIp;
+    tChecksumCheckResult res;
     tTcpIpPacketParsingResult ppr;
     ULONG flagsToCalculate = 0;
     res.value = 0;
-    resIp.value = 0;
 
     //VIRTIO_NET_HDR_F_NEEDS_CSUM - we need to calculate TCP/UDP CS
     //VIRTIO_NET_HDR_F_DATA_VALID - host tells us TCP/UDP CS is OK
 
     if (f.fRxIPChecksum) flagsToCalculate |= pcrIpChecksum; // check only
@@ -2248,10 +2247,17 @@ tChecksumCheckResult ParaNdis_CheckRxChecksum(
         }
     }
 
     ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE, ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate, __FUNCTION__);
 
+    if (ppr.ipCheckSum == ppresIPTooShort || ppr.xxpStatus == ppresXxpIncomplete)
+    {
+        res.flags.IpOK = FALSE;
+        res.flags.IpFailed = TRUE;
+        return res;
+    }
+
     if (virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID)
     {
         pContext->extraStatistics.framesRxCSHwOK++;
         ppr.xxpCheckSum = ppresCSOK;
     }
diff --git a/NetKVM/Common/sw-offload.cpp b/NetKVM/Common/sw-offload.cpp
index 0076c08..f2e9045 100644
--- a/NetKVM/Common/sw-offload.cpp
+++ b/NetKVM/Common/sw-offload.cpp
@@ -154,68 +154,95 @@ static __inline tTcpIpPacketParsingResult
 ProcessTCPHeader(tTcpIpPacketParsingResult _res, PVOID pIpHeader, ULONG len, USHORT ipHeaderSize)
 {
     ULONG tcpipDataAt;
     tTcpIpPacketParsingResult res = _res;
     tcpipDataAt = ipHeaderSize + sizeof(TCPHeader);
-    res.xxpStatus = ppresXxpIncomplete;
     res.TcpUdp = ppresIsTCP;
 
     if (len >= tcpipDataAt)
     {
         TCPHeader *pTcpHeader = (TCPHeader *)RtlOffsetToPointer(pIpHeader, ipHeaderSize);
         res.xxpStatus = ppresXxpKnown;
+        res.xxpFull = TRUE;
         tcpipDataAt = ipHeaderSize + TCP_HEADER_LENGTH(pTcpHeader);
         res.XxpIpHeaderSize = tcpipDataAt;
     }
     else
     {
         DPrintf(2, ("tcp: %d < min headers %d\n", len, tcpipDataAt));
+        res.xxpFull = FALSE;
+        res.xxpStatus = ppresXxpIncomplete;
     }
     return res;
 }
 
 static __inline tTcpIpPacketParsingResult
 ProcessUDPHeader(tTcpIpPacketParsingResult _res, PVOID pIpHeader, ULONG len, USHORT ipHeaderSize)
 {
     tTcpIpPacketParsingResult res = _res;
     ULONG udpDataStart = ipHeaderSize + sizeof(UDPHeader);
-    res.xxpStatus = ppresXxpIncomplete;
     res.TcpUdp = ppresIsUDP;
     res.XxpIpHeaderSize = udpDataStart;
     if (len >= udpDataStart)
     {
         UDPHeader *pUdpHeader = (UDPHeader *)RtlOffsetToPointer(pIpHeader, ipHeaderSize);
         USHORT datagramLength = swap_short(pUdpHeader->udp_length);
         res.xxpStatus = ppresXxpKnown;
+        res.xxpFull = TRUE;
         // may be full or not, but the datagram length is known
         DPrintf(2, ("udp: len %d, datagramLength %d\n", len, datagramLength));
     }
+    else
+    {
+        res.xxpFull = FALSE;
+        res.xxpStatus = ppresXxpIncomplete;
+    }
     return res;
 }
 
 static __inline tTcpIpPacketParsingResult
 QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
 {
     tTcpIpPacketParsingResult res;
+    res.value = 0;
+
+    if (len < 4)
+    {
+        res.ipStatus = ppresNotIP;
+        return res;
+    }
+
     UCHAR  ver_len = pIpHeader->v4.ip_verlen;
     UCHAR  ip_version = (ver_len & 0xF0) >> 4;
     USHORT ipHeaderSize = 0;
     USHORT fullLength = 0;
     res.value = 0;
-    
+
     if (ip_version == 4)
     {
+        if (len < sizeof(IPv4Header))
+        {
+            res.ipStatus = ppresNotIP;
+            return res;
+        }
         ipHeaderSize = (ver_len & 0xF) << 2;
         fullLength = swap_short(pIpHeader->v4.ip_length);
-        DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d\n",
-            ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength));
+        DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n",
+            ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));
+
         res.ipStatus = (ipHeaderSize >= sizeof(IPv4Header)) ? ppresIPV4 : ppresNotIP;
-        if (len < ipHeaderSize) res.ipCheckSum = ppresIPTooShort;
-        if (fullLength) {}
-        else
+        if (res.ipStatus == ppresNotIP)
         {
-            DPrintf(2, ("ip v.%d, iplen %d\n", ip_version, fullLength));
+            return res;
+        }
+
+        if (ipHeaderSize >= fullLength || len < fullLength)
+        {
+            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n",
+                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));
+            res.ipCheckSum = ppresIPTooShort;
+            return res;
         }
     }
     else if (ip_version == 6)
     {
         UCHAR nextHeader = pIpHeader->v6.ip6_next_header;
@@ -289,11 +316,11 @@ QualifyIpPacket(IPHeader *pIpHeader, ULONG len)
     }
     
     if (res.ipStatus == ppresIPV4)
     {
         res.ipHeaderSize = ipHeaderSize;
-        res.xxpFull = len >= fullLength ? 1 : 0;
+
         // bit "more fragments" or fragment offset mean the packet is fragmented
         res.IsFragment = (pIpHeader->v4.ip_offset & ~0xC0) != 0;
         switch (pIpHeader->v4.ip_protocol)
         {
             case PROTOCOL_TCP:
@@ -613,10 +640,13 @@ tTcpIpPacketParsingResult ParaNdis_CheckSumVerify(
                                                 LPCSTR caller)
 {
     IPHeader *pIpHeader = (IPHeader *) RtlOffsetToPointer(pDataPages[0].Virtual, ulStartOffset);
 
     tTcpIpPacketParsingResult res = QualifyIpPacket(pIpHeader, ulDataLength);
+    if (res.ipStatus == ppresNotIP || res.ipCheckSum == ppresIPTooShort)
+        return res;
+
     if (res.ipStatus == ppresIPV4)
     {
         if (flags & pcrIpChecksum)
             res = VerifyIpChecksum(&pIpHeader->v4, res, (flags & pcrFixIPChecksum) != 0);
         if(res.xxpStatus == ppresXxpKnown)

Vulnerable code:     tChecksumCheckResult res, resIp;
    resIp.value = 0;
    res.xxpStatus = ppresXxpIncomplete;
    res.xxpStatus = ppresXxpIncomplete;
    
        DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d\n",
            ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength));
        if (len < ipHeaderSize) res.ipCheckSum = ppresIPTooShort;
        if (fullLength) {}
        else
            DPrintf(2, ("ip v.%d, iplen %d\n", ip_version, fullLength));
        res.xxpFull = len >= fullLength ? 1 : 0;
Fixed code:     tChecksumCheckResult res;
    if (ppr.ipCheckSum == ppresIPTooShort || ppr.xxpStatus == ppresXxpIncomplete)
    {
        res.flags.IpOK = FALSE;
        res.flags.IpFailed = TRUE;
        return res;
    }

        res.xxpFull = TRUE;
        res.xxpFull = FALSE;
        res.xxpStatus = ppresXxpIncomplete;
        res.xxpFull = TRUE;
    else
    {
        res.xxpFull = FALSE;
        res.xxpStatus = ppresXxpIncomplete;
    }
    res.value = 0;

    if (len < 4)
    {
        res.ipStatus = ppresNotIP;
        return res;
    }


        if (len < sizeof(IPv4Header))
        {
            res.ipStatus = ppresNotIP;
            return res;
        }
        DPrintf(3, ("ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n",
            ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));

        if (res.ipStatus == ppresNotIP)
            return res;
        }

        if (ipHeaderSize >= fullLength || len < fullLength)
        {
            DPrintf(2, ("[%s] - truncated packet - ip_version %d, ipHeaderSize %d, protocol %d, iplen %d, L2 payload length %d\n",
                ip_version, ipHeaderSize, pIpHeader->v4.ip_protocol, fullLength, len));
            res.ipCheckSum = ppresIPTooShort;
            return res;

    if (res.ipStatus == ppresNotIP || res.ipCheckSum == ppresIPTooShort)
        return res;