From owner-svn-src-all@freebsd.org Tue Aug 6 17:13:18 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id DC32DC6024; Tue, 6 Aug 2019 17:13:18 +0000 (UTC) (envelope-from gordon@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4631Ry5Kwzz4F5r; Tue, 6 Aug 2019 17:13:18 +0000 (UTC) (envelope-from gordon@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 98BD82F2ED; Tue, 6 Aug 2019 17:13:18 +0000 (UTC) (envelope-from gordon@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x76HDI9K043901; Tue, 6 Aug 2019 17:13:18 GMT (envelope-from gordon@FreeBSD.org) Received: (from gordon@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x76HDHDi043896; Tue, 6 Aug 2019 17:13:17 GMT (envelope-from gordon@FreeBSD.org) Message-Id: <201908061713.x76HDHDi043896@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: gordon set sender to gordon@FreeBSD.org using -f From: Gordon Tetlow Date: Tue, 6 Aug 2019 17:13:17 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org Subject: svn commit: r350647 - in releng: 11.2/usr.sbin/bhyve 11.3/usr.sbin/bhyve 12.0/usr.sbin/bhyve X-SVN-Group: releng X-SVN-Commit-Author: gordon X-SVN-Commit-Paths: in releng: 11.2/usr.sbin/bhyve 11.3/usr.sbin/bhyve 12.0/usr.sbin/bhyve X-SVN-Commit-Revision: 350647 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 06 Aug 2019 17:13:18 -0000 Author: gordon Date: Tue Aug 6 17:13:17 2019 New Revision: 350647 URL: https://svnweb.freebsd.org/changeset/base/350647 Log: Fix insufficient validation of guest-supplied data (e1000 device). Approved by: so Security: FreeBSD-SA-19:21.bhyve Security: CVE-2019-5609 Modified: releng/11.2/usr.sbin/bhyve/pci_e82545.c releng/11.3/usr.sbin/bhyve/pci_e82545.c releng/12.0/usr.sbin/bhyve/pci_e82545.c Modified: releng/11.2/usr.sbin/bhyve/pci_e82545.c ============================================================================== --- releng/11.2/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:12:17 2019 (r350646) +++ releng/11.2/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:13:17 2019 (r350647) @@ -1076,8 +1076,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head struct ck_info ckinfo[2]; struct iovec *iov; union e1000_tx_udesc *dsc; - int desc, dtype, len, ntype, iovcnt, tlen, hdrlen, vlen, tcp, tso; + int desc, dtype, len, ntype, iovcnt, tlen, tcp, tso; int mss, paylen, seg, tiovcnt, left, now, nleft, nnow, pv, pvoff; + unsigned hdrlen, vlen; uint32_t tcpsum, tcpseq; uint16_t ipcs, tcpcs, ipid, ohead; @@ -1221,6 +1222,68 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head } else { /* In case of TSO header length provided by software. */ hdrlen = sc->esc_txctx.tcp_seg_setup.fields.hdr_len; + + /* + * Cap the header length at 240 based on 7.2.4.5 of + * the Intel 82576EB (Rev 2.63) datasheet. + */ + if (hdrlen > 240) { + WPRINTF("TSO hdrlen too large: %d\r\n", hdrlen); + goto done; + } + + /* + * If VLAN insertion is requested, ensure the header + * at least holds the amount of data copied during + * VLAN insertion below. + * + * XXX: Realistic packets will include a full Ethernet + * header before the IP header at ckinfo[0].ck_start, + * but this check is sufficient to prevent + * out-of-bounds access below. + */ + if (vlen != 0 && hdrlen < ETHER_ADDR_LEN*2) { + WPRINTF("TSO hdrlen too small for vlan insertion " + "(%d vs %d) -- dropped\r\n", hdrlen, + ETHER_ADDR_LEN*2); + goto done; + } + + /* + * Ensure that the header length covers the used fields + * in the IP and TCP headers as well as the IP and TCP + * checksums. The following fields are accessed below: + * + * Header | Field | Offset | Length + * -------+-------+--------+------- + * IPv4 | len | 2 | 2 + * IPv4 | ID | 4 | 2 + * IPv6 | len | 4 | 2 + * TCP | seq # | 4 | 4 + * TCP | flags | 13 | 1 + * UDP | len | 4 | 4 + */ + if (hdrlen < ckinfo[0].ck_start + 6 || + hdrlen < ckinfo[0].ck_off + 2) { + WPRINTF("TSO hdrlen too small for IP fields (%d) " + "-- dropped\r\n", hdrlen); + goto done; + } + if (sc->esc_txctx.cmd_and_length & E1000_TXD_CMD_TCP) { + if (hdrlen < ckinfo[1].ck_start + 14 || + (ckinfo[1].ck_valid && + hdrlen < ckinfo[1].ck_off + 2)) { + WPRINTF("TSO hdrlen too small for TCP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } else { + if (hdrlen < ckinfo[1].ck_start + 8) { + WPRINTF("TSO hdrlen too small for UDP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } } /* Allocate, fill and prepend writable header vector. */ @@ -1242,7 +1305,8 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head iovcnt++; iov->iov_base = hdr; iov->iov_len = hdrlen; - } + } else + hdr = NULL; /* Insert VLAN tag. */ if (vlen != 0) { @@ -1284,7 +1348,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head DPRINTF("tx %s segmentation offload %d+%d/%d bytes %d iovs\r\n", tcp ? "TCP" : "UDP", hdrlen, paylen, mss, iovcnt); ipid = ntohs(*(uint16_t *)&hdr[ckinfo[0].ck_start + 4]); - tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); + tcpseq = 0; + if (tcp) + tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); ipcs = *(uint16_t *)&hdr[ckinfo[0].ck_off]; tcpcs = 0; if (ckinfo[1].ck_valid) /* Save partial pseudo-header checksum. */ Modified: releng/11.3/usr.sbin/bhyve/pci_e82545.c ============================================================================== --- releng/11.3/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:12:17 2019 (r350646) +++ releng/11.3/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:13:17 2019 (r350647) @@ -1078,8 +1078,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head struct ck_info ckinfo[2]; struct iovec *iov; union e1000_tx_udesc *dsc; - int desc, dtype, len, ntype, iovcnt, tlen, hdrlen, vlen, tcp, tso; + int desc, dtype, len, ntype, iovcnt, tlen, tcp, tso; int mss, paylen, seg, tiovcnt, left, now, nleft, nnow, pv, pvoff; + unsigned hdrlen, vlen; uint32_t tcpsum, tcpseq; uint16_t ipcs, tcpcs, ipid, ohead; @@ -1223,6 +1224,68 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head } else { /* In case of TSO header length provided by software. */ hdrlen = sc->esc_txctx.tcp_seg_setup.fields.hdr_len; + + /* + * Cap the header length at 240 based on 7.2.4.5 of + * the Intel 82576EB (Rev 2.63) datasheet. + */ + if (hdrlen > 240) { + WPRINTF("TSO hdrlen too large: %d\r\n", hdrlen); + goto done; + } + + /* + * If VLAN insertion is requested, ensure the header + * at least holds the amount of data copied during + * VLAN insertion below. + * + * XXX: Realistic packets will include a full Ethernet + * header before the IP header at ckinfo[0].ck_start, + * but this check is sufficient to prevent + * out-of-bounds access below. + */ + if (vlen != 0 && hdrlen < ETHER_ADDR_LEN*2) { + WPRINTF("TSO hdrlen too small for vlan insertion " + "(%d vs %d) -- dropped\r\n", hdrlen, + ETHER_ADDR_LEN*2); + goto done; + } + + /* + * Ensure that the header length covers the used fields + * in the IP and TCP headers as well as the IP and TCP + * checksums. The following fields are accessed below: + * + * Header | Field | Offset | Length + * -------+-------+--------+------- + * IPv4 | len | 2 | 2 + * IPv4 | ID | 4 | 2 + * IPv6 | len | 4 | 2 + * TCP | seq # | 4 | 4 + * TCP | flags | 13 | 1 + * UDP | len | 4 | 4 + */ + if (hdrlen < ckinfo[0].ck_start + 6 || + hdrlen < ckinfo[0].ck_off + 2) { + WPRINTF("TSO hdrlen too small for IP fields (%d) " + "-- dropped\r\n", hdrlen); + goto done; + } + if (sc->esc_txctx.cmd_and_length & E1000_TXD_CMD_TCP) { + if (hdrlen < ckinfo[1].ck_start + 14 || + (ckinfo[1].ck_valid && + hdrlen < ckinfo[1].ck_off + 2)) { + WPRINTF("TSO hdrlen too small for TCP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } else { + if (hdrlen < ckinfo[1].ck_start + 8) { + WPRINTF("TSO hdrlen too small for UDP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } } /* Allocate, fill and prepend writable header vector. */ @@ -1244,7 +1307,8 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head iovcnt++; iov->iov_base = hdr; iov->iov_len = hdrlen; - } + } else + hdr = NULL; /* Insert VLAN tag. */ if (vlen != 0) { @@ -1286,7 +1350,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head DPRINTF("tx %s segmentation offload %d+%d/%d bytes %d iovs\r\n", tcp ? "TCP" : "UDP", hdrlen, paylen, mss, iovcnt); ipid = ntohs(*(uint16_t *)&hdr[ckinfo[0].ck_start + 4]); - tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); + tcpseq = 0; + if (tcp) + tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); ipcs = *(uint16_t *)&hdr[ckinfo[0].ck_off]; tcpcs = 0; if (ckinfo[1].ck_valid) /* Save partial pseudo-header checksum. */ Modified: releng/12.0/usr.sbin/bhyve/pci_e82545.c ============================================================================== --- releng/12.0/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:12:17 2019 (r350646) +++ releng/12.0/usr.sbin/bhyve/pci_e82545.c Tue Aug 6 17:13:17 2019 (r350647) @@ -1078,8 +1078,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head struct ck_info ckinfo[2]; struct iovec *iov; union e1000_tx_udesc *dsc; - int desc, dtype, len, ntype, iovcnt, tlen, hdrlen, vlen, tcp, tso; + int desc, dtype, len, ntype, iovcnt, tlen, tcp, tso; int mss, paylen, seg, tiovcnt, left, now, nleft, nnow, pv, pvoff; + unsigned hdrlen, vlen; uint32_t tcpsum, tcpseq; uint16_t ipcs, tcpcs, ipid, ohead; @@ -1223,6 +1224,68 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head } else { /* In case of TSO header length provided by software. */ hdrlen = sc->esc_txctx.tcp_seg_setup.fields.hdr_len; + + /* + * Cap the header length at 240 based on 7.2.4.5 of + * the Intel 82576EB (Rev 2.63) datasheet. + */ + if (hdrlen > 240) { + WPRINTF("TSO hdrlen too large: %d\r\n", hdrlen); + goto done; + } + + /* + * If VLAN insertion is requested, ensure the header + * at least holds the amount of data copied during + * VLAN insertion below. + * + * XXX: Realistic packets will include a full Ethernet + * header before the IP header at ckinfo[0].ck_start, + * but this check is sufficient to prevent + * out-of-bounds access below. + */ + if (vlen != 0 && hdrlen < ETHER_ADDR_LEN*2) { + WPRINTF("TSO hdrlen too small for vlan insertion " + "(%d vs %d) -- dropped\r\n", hdrlen, + ETHER_ADDR_LEN*2); + goto done; + } + + /* + * Ensure that the header length covers the used fields + * in the IP and TCP headers as well as the IP and TCP + * checksums. The following fields are accessed below: + * + * Header | Field | Offset | Length + * -------+-------+--------+------- + * IPv4 | len | 2 | 2 + * IPv4 | ID | 4 | 2 + * IPv6 | len | 4 | 2 + * TCP | seq # | 4 | 4 + * TCP | flags | 13 | 1 + * UDP | len | 4 | 4 + */ + if (hdrlen < ckinfo[0].ck_start + 6 || + hdrlen < ckinfo[0].ck_off + 2) { + WPRINTF("TSO hdrlen too small for IP fields (%d) " + "-- dropped\r\n", hdrlen); + goto done; + } + if (sc->esc_txctx.cmd_and_length & E1000_TXD_CMD_TCP) { + if (hdrlen < ckinfo[1].ck_start + 14 || + (ckinfo[1].ck_valid && + hdrlen < ckinfo[1].ck_off + 2)) { + WPRINTF("TSO hdrlen too small for TCP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } else { + if (hdrlen < ckinfo[1].ck_start + 8) { + WPRINTF("TSO hdrlen too small for UDP fields " + "(%d) -- dropped\r\n", hdrlen); + goto done; + } + } } /* Allocate, fill and prepend writable header vector. */ @@ -1244,7 +1307,8 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head iovcnt++; iov->iov_base = hdr; iov->iov_len = hdrlen; - } + } else + hdr = NULL; /* Insert VLAN tag. */ if (vlen != 0) { @@ -1286,7 +1350,9 @@ e82545_transmit(struct e82545_softc *sc, uint16_t head DPRINTF("tx %s segmentation offload %d+%d/%d bytes %d iovs\r\n", tcp ? "TCP" : "UDP", hdrlen, paylen, mss, iovcnt); ipid = ntohs(*(uint16_t *)&hdr[ckinfo[0].ck_start + 4]); - tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); + tcpseq = 0; + if (tcp) + tcpseq = ntohl(*(uint32_t *)&hdr[ckinfo[1].ck_start + 4]); ipcs = *(uint16_t *)&hdr[ckinfo[0].ck_off]; tcpcs = 0; if (ckinfo[1].ck_valid) /* Save partial pseudo-header checksum. */