svn commit: r209959 - head/sys/dev/e1000

Jack F Vogel jfv at FreeBSD.org
Mon Jul 12 21:47:31 UTC 2010


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);


More information about the svn-src-head mailing list