summaryrefslogtreecommitdiffstats
path: root/sys/dev/e1000
diff options
context:
space:
mode:
authorjfv <jfv@FreeBSD.org>2010-07-12 21:47:30 +0000
committerjfv <jfv@FreeBSD.org>2010-07-12 21:47:30 +0000
commit27b230a9a37c96fef97b0bba8c03f7276a263746 (patch)
treed2f1efc09c50df6d41901fe868b03cfd6d5add14 /sys/dev/e1000
parent0c373eb9d42ec506b9d662275f40cf6c61567b71 (diff)
downloadFreeBSD-src-27b230a9a37c96fef97b0bba8c03f7276a263746.zip
FreeBSD-src-27b230a9a37c96fef97b0bba8c03f7276a263746.tar.gz
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
Diffstat (limited to 'sys/dev/e1000')
-rw-r--r--sys/dev/e1000/if_em.c28
-rw-r--r--sys/dev/e1000/if_lem.c26
2 files changed, 35 insertions, 19 deletions
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 84a268c..e520512 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -1738,6 +1738,19 @@ em_xmit(struct tx_ring *txr, struct mbuf **m_headp)
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_ring *txr, struct mbuf *mp,
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_ring *txr, struct mbuf *mp,
*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_ring *txr, struct mbuf *mp,
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);
diff --git a/sys/dev/e1000/if_lem.c b/sys/dev/e1000/if_lem.c
index c0a53cf..1f03f7b 100644
--- a/sys/dev/e1000/if_lem.c
+++ b/sys/dev/e1000/if_lem.c
@@ -1566,6 +1566,19 @@ lem_xmit(struct adapter *adapter, struct mbuf **m_headp)
}
/*
+ ** 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 adapter *adapter, struct mbuf *mp,
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 adapter *adapter, struct mbuf *mp,
*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 adapter *adapter, struct mbuf *mp,
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 adapter *adapter, struct mbuf *mp,
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);
OpenPOWER on IntegriCloud