Date: Mon, 12 Jul 2010 21:47:30 +0000 (UTC) From: Jack F Vogel <jfv@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r209959 - head/sys/dev/e1000 Message-ID: <201007122147.o6CLlUeR026405@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201007122147.o6CLlUeR026405>