Author: jfv
Date: Mon Jul 12 21:47:30 2010
New Revision: 209959
URL: http://svn.freebsd.org/changeset/base/209959

Log:
  Fix for a panic when TX checksum offload is done and
  a packet has only a header in the first mbuf, the
  checksum code will dereference a pointer into the
  non-existing IP header. Do a check for the size and
  pullup if needed. Thanks to Michael Tuexen for this
  fix.
  
  MFC: asap - should be in 8.1 IMHO

Modified:
  head/sys/dev/e1000/if_em.c
  head/sys/dev/e1000/if_lem.c

Modified: head/sys/dev/e1000/if_em.c
==============================================================================
--- head/sys/dev/e1000/if_em.c  Mon Jul 12 21:09:55 2010        (r209958)
+++ head/sys/dev/e1000/if_em.c  Mon Jul 12 21:47:30 2010        (r209959)
@@ -1738,6 +1738,19 @@ em_xmit(struct tx_ring *txr, struct mbuf
        do_tso = ((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0);
 
        /*
+       ** When doing checksum offload, it is critical to
+       ** make sure the first mbuf has more than header,
+       ** because that routine expects data to be present.
+       */
+       if ((m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) &&
+           (m_head->m_len < ETHER_HDR_LEN + sizeof(struct ip))) {
+               m_head = m_pullup(m_head, ETHER_HDR_LEN + sizeof(struct ip));
+               *m_headp = m_head;
+               if (m_head == NULL)
+                       return (ENOBUFS);
+       }
+
+       /*
         * TSO workaround: 
         *  If an mbuf is only header we need  
         *     to pull 4 bytes of data into it. 
@@ -3262,6 +3275,7 @@ em_transmit_checksum_setup(struct tx_rin
 
 
        cmd = hdr_len = ipproto = 0;
+       *txd_upper = *txd_lower = 0;
        cur = txr->next_avail_desc;
 
        /*
@@ -3305,29 +3319,21 @@ em_transmit_checksum_setup(struct tx_rin
                        *txd_upper |= E1000_TXD_POPTS_IXSM << 8;
                }
 
-               if (mp->m_len < ehdrlen + ip_hlen)
-                       return; /* failure */
-
                hdr_len = ehdrlen + ip_hlen;
                ipproto = ip->ip_p;
-
                break;
+
        case ETHERTYPE_IPV6:
                ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
                ip_hlen = sizeof(struct ip6_hdr); /* XXX: No header stacking. */
 
-               if (mp->m_len < ehdrlen + ip_hlen)
-                       return; /* failure */
-
                /* IPv6 doesn't have a header checksum. */
 
                hdr_len = ehdrlen + ip_hlen;
                ipproto = ip6->ip6_nxt;
-
                break;
+
        default:
-               *txd_upper = 0;
-               *txd_lower = 0;
                return;
        }
 
@@ -3381,6 +3387,8 @@ em_transmit_checksum_setup(struct tx_rin
                break;
        }
 
+       if (TXD == NULL)
+               return;
        TXD->tcp_seg_setup.data = htole32(0);
        TXD->cmd_and_length =
            htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT | cmd);

Modified: head/sys/dev/e1000/if_lem.c
==============================================================================
--- head/sys/dev/e1000/if_lem.c Mon Jul 12 21:09:55 2010        (r209958)
+++ head/sys/dev/e1000/if_lem.c Mon Jul 12 21:47:30 2010        (r209959)
@@ -1566,6 +1566,19 @@ lem_xmit(struct adapter *adapter, struct
        }
 
        /*
+       ** When doing checksum offload, it is critical to
+       ** make sure the first mbuf has more than header,
+       ** because that routine expects data to be present.
+       */
+       if ((m_head->m_pkthdr.csum_flags & CSUM_OFFLOAD) &&
+           (m_head->m_len < ETHER_HDR_LEN + sizeof(struct ip))) {
+               m_head = m_pullup(m_head, ETHER_HDR_LEN + sizeof(struct ip));
+               *m_headp = m_head;
+               if (m_head == NULL)
+                       return (ENOBUFS);
+       }
+
+       /*
         * Map the packet for DMA
         *
         * Capture the first descriptor index,
@@ -2851,6 +2864,7 @@ lem_transmit_checksum_setup(struct adapt
 
 
        cmd = hdr_len = ipproto = 0;
+       *txd_upper = *txd_lower = 0;
        curr_txd = adapter->next_avail_tx_desc;
 
        /*
@@ -2894,9 +2908,6 @@ lem_transmit_checksum_setup(struct adapt
                        *txd_upper |= E1000_TXD_POPTS_IXSM << 8;
                }
 
-               if (mp->m_len < ehdrlen + ip_hlen)
-                       return; /* failure */
-
                hdr_len = ehdrlen + ip_hlen;
                ipproto = ip->ip_p;
 
@@ -2905,18 +2916,13 @@ lem_transmit_checksum_setup(struct adapt
                ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen);
                ip_hlen = sizeof(struct ip6_hdr); /* XXX: No header stacking. */
 
-               if (mp->m_len < ehdrlen + ip_hlen)
-                       return; /* failure */
-
                /* IPv6 doesn't have a header checksum. */
 
                hdr_len = ehdrlen + ip_hlen;
                ipproto = ip6->ip6_nxt;
-
                break;
+
        default:
-               *txd_upper = 0;
-               *txd_lower = 0;
                return;
        }
 
@@ -2970,6 +2976,8 @@ lem_transmit_checksum_setup(struct adapt
                break;
        }
 
+       if (TXD == NULL)
+               return;
        TXD->tcp_seg_setup.data = htole32(0);
        TXD->cmd_and_length =
            htole32(adapter->txd_cmd | E1000_TXD_CMD_DEXT | cmd);
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to