diff options
author | jfv <jfv@FreeBSD.org> | 2010-07-12 21:47:30 +0000 |
---|---|---|
committer | jfv <jfv@FreeBSD.org> | 2010-07-12 21:47:30 +0000 |
commit | 27b230a9a37c96fef97b0bba8c03f7276a263746 (patch) | |
tree | d2f1efc09c50df6d41901fe868b03cfd6d5add14 /sys/dev/e1000 | |
parent | 0c373eb9d42ec506b9d662275f40cf6c61567b71 (diff) | |
download | FreeBSD-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.c | 28 | ||||
-rw-r--r-- | sys/dev/e1000/if_lem.c | 26 |
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); |