From owner-svn-src-all@freebsd.org Tue Aug 6 17:11: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 349F2C5E6D; Tue, 6 Aug 2019 17:11: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 4631Pf1Jpxz4DdG; Tue, 6 Aug 2019 17:11: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 E899D2F15A; Tue, 6 Aug 2019 17:11:17 +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 x76HBHiU039010; Tue, 6 Aug 2019 17:11:17 GMT (envelope-from gordon@FreeBSD.org) Received: (from gordon@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x76HBHa6039007; Tue, 6 Aug 2019 17:11:17 GMT (envelope-from gordon@FreeBSD.org) Message-Id: <201908061711.x76HBHa6039007@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:11:17 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-releng@freebsd.org Subject: svn commit: r350644 - in releng: 11.2/sys/netinet6 11.3/sys/netinet6 12.0/sys/netinet6 X-SVN-Group: releng X-SVN-Commit-Author: gordon X-SVN-Commit-Paths: in releng: 11.2/sys/netinet6 11.3/sys/netinet6 12.0/sys/netinet6 X-SVN-Commit-Revision: 350644 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:11:18 -0000 Author: gordon Date: Tue Aug 6 17:11:17 2019 New Revision: 350644 URL: https://svnweb.freebsd.org/changeset/base/350644 Log: Fix ICMPv6 / MLDv2 out-of-bounds memory access. Approved by: so Security: FreeBSD-SA-19:19.mldv2 Security: CVE-2019-5608 Modified: releng/11.2/sys/netinet6/mld6.c releng/11.3/sys/netinet6/mld6.c releng/12.0/sys/netinet6/mld6.c Modified: releng/11.2/sys/netinet6/mld6.c ============================================================================== --- releng/11.2/sys/netinet6/mld6.c Tue Aug 6 17:09:47 2019 (r350643) +++ releng/11.2/sys/netinet6/mld6.c Tue Aug 6 17:11:17 2019 (r350644) @@ -137,14 +137,15 @@ static int mld_v2_enqueue_group_record(struct mbufq *, struct in6_multi *, const int, const int, const int, const int); static int mld_v2_input_query(struct ifnet *, const struct ip6_hdr *, - struct mbuf *, const int, const int); + struct mbuf *, struct mldv2_query *, const int, const int); static int mld_v2_merge_state_changes(struct in6_multi *, struct mbufq *); static void mld_v2_process_group_timers(struct mld_ifsoftc *, struct mbufq *, struct mbufq *, struct in6_multi *, const int); static int mld_v2_process_group_query(struct in6_multi *, - struct mld_ifsoftc *mli, int, struct mbuf *, const int); + struct mld_ifsoftc *mli, int, struct mbuf *, + struct mldv2_query *, const int); static int sysctl_mld_gsr(SYSCTL_HANDLER_ARGS); static int sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS); @@ -794,16 +795,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t * Process a received MLDv2 general, group-specific or * group-and-source-specific query. * - * Assumes that the query header has been pulled up to sizeof(mldv2_query). + * Assumes that mld points to a struct mldv2_query which is stored in + * contiguous memory. * * Return 0 if successful, otherwise an appropriate error code is returned. */ static int mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, - struct mbuf *m, const int off, const int icmp6len) + struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len) { struct mld_ifsoftc *mli; - struct mldv2_query *mld; struct in6_multi *inm; uint32_t maxdelay, nsrc, qqi; int is_general_query; @@ -828,8 +829,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp)); - mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off); - maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */ if (maxdelay >= 32768) { maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) << @@ -954,7 +953,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 * group-specific or group-and-source query. */ if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) - mld_v2_process_group_query(inm, mli, timer, m, off); + mld_v2_process_group_query(inm, mli, timer, m, mld, off); /* XXX Clear embedded scope ID as userland won't expect it. */ in6_clearscope(&mld->mld_addr); @@ -975,9 +974,8 @@ out_locked: */ static int mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli, - int timer, struct mbuf *m0, const int off) + int timer, struct mbuf *m0, struct mldv2_query *mld, const int off) { - struct mldv2_query *mld; int retval; uint16_t nsrc; @@ -985,7 +983,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru MLD_LOCK_ASSERT(); retval = 0; - mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off); switch (inm->in6m_state) { case MLD_NOT_MEMBER: @@ -1005,6 +1002,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru nsrc = ntohs(mld->mld_numsrc); + /* Length should be checked by calling function. */ + KASSERT((m0->m_flags & M_PKTHDR) == 0 || + m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), + ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)", + m0->m_pkthdr.len, off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), m0)); + + /* * Deal with group-specific queries upfront. * If any group query is already pending, purge any recorded @@ -1046,28 +1052,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru * report for those sources. */ if (inm->in6m_nsrc > 0) { - struct mbuf *m; - uint8_t *sp; + struct in6_addr srcaddr; int i, nrecorded; int soff; - m = m0; soff = off + sizeof(struct mldv2_query); nrecorded = 0; for (i = 0; i < nsrc; i++) { - sp = mtod(m, uint8_t *) + soff; - retval = in6m_record_source(inm, - (const struct in6_addr *)sp); + m_copydata(m0, soff, sizeof(struct in6_addr), + (caddr_t)&srcaddr); + retval = in6m_record_source(inm, &srcaddr); if (retval < 0) break; nrecorded += retval; soff += sizeof(struct in6_addr); - if (soff >= m->m_len) { - soff = soff - m->m_len; - m = m->m_next; - if (m == NULL) - break; - } } if (nrecorded > 0) { CTR1(KTR_MLD, @@ -1276,8 +1274,8 @@ mld_input(struct mbuf *m, int off, int icmp6len) if (mld_v1_input_query(ifp, ip6, mld) != 0) return (0); } else if (icmp6len >= sizeof(struct mldv2_query)) { - if (mld_v2_input_query(ifp, ip6, m, off, - icmp6len) != 0) + if (mld_v2_input_query(ifp, ip6, m, + (struct mldv2_query *)mld, off, icmp6len) != 0) return (0); } break; Modified: releng/11.3/sys/netinet6/mld6.c ============================================================================== --- releng/11.3/sys/netinet6/mld6.c Tue Aug 6 17:09:47 2019 (r350643) +++ releng/11.3/sys/netinet6/mld6.c Tue Aug 6 17:11:17 2019 (r350644) @@ -137,14 +137,15 @@ static int mld_v2_enqueue_group_record(struct mbufq *, struct in6_multi *, const int, const int, const int, const int); static int mld_v2_input_query(struct ifnet *, const struct ip6_hdr *, - struct mbuf *, const int, const int); + struct mbuf *, struct mldv2_query *, const int, const int); static int mld_v2_merge_state_changes(struct in6_multi *, struct mbufq *); static void mld_v2_process_group_timers(struct mld_ifsoftc *, struct mbufq *, struct mbufq *, struct in6_multi *, const int); static int mld_v2_process_group_query(struct in6_multi *, - struct mld_ifsoftc *mli, int, struct mbuf *, const int); + struct mld_ifsoftc *mli, int, struct mbuf *, + struct mldv2_query *, const int); static int sysctl_mld_gsr(SYSCTL_HANDLER_ARGS); static int sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS); @@ -794,16 +795,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t * Process a received MLDv2 general, group-specific or * group-and-source-specific query. * - * Assumes that the query header has been pulled up to sizeof(mldv2_query). + * Assumes that mld points to a struct mldv2_query which is stored in + * contiguous memory. * * Return 0 if successful, otherwise an appropriate error code is returned. */ static int mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, - struct mbuf *m, const int off, const int icmp6len) + struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len) { struct mld_ifsoftc *mli; - struct mldv2_query *mld; struct in6_multi *inm; uint32_t maxdelay, nsrc, qqi; int is_general_query; @@ -828,8 +829,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp)); - mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off); - maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */ if (maxdelay >= 32768) { maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) << @@ -954,7 +953,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 * group-specific or group-and-source query. */ if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) - mld_v2_process_group_query(inm, mli, timer, m, off); + mld_v2_process_group_query(inm, mli, timer, m, mld, off); /* XXX Clear embedded scope ID as userland won't expect it. */ in6_clearscope(&mld->mld_addr); @@ -975,9 +974,8 @@ out_locked: */ static int mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli, - int timer, struct mbuf *m0, const int off) + int timer, struct mbuf *m0, struct mldv2_query *mld, const int off) { - struct mldv2_query *mld; int retval; uint16_t nsrc; @@ -985,7 +983,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru MLD_LOCK_ASSERT(); retval = 0; - mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off); switch (inm->in6m_state) { case MLD_NOT_MEMBER: @@ -1005,6 +1002,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru nsrc = ntohs(mld->mld_numsrc); + /* Length should be checked by calling function. */ + KASSERT((m0->m_flags & M_PKTHDR) == 0 || + m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), + ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)", + m0->m_pkthdr.len, off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), m0)); + + /* * Deal with group-specific queries upfront. * If any group query is already pending, purge any recorded @@ -1046,28 +1052,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru * report for those sources. */ if (inm->in6m_nsrc > 0) { - struct mbuf *m; - uint8_t *sp; + struct in6_addr srcaddr; int i, nrecorded; int soff; - m = m0; soff = off + sizeof(struct mldv2_query); nrecorded = 0; for (i = 0; i < nsrc; i++) { - sp = mtod(m, uint8_t *) + soff; - retval = in6m_record_source(inm, - (const struct in6_addr *)sp); + m_copydata(m0, soff, sizeof(struct in6_addr), + (caddr_t)&srcaddr); + retval = in6m_record_source(inm, &srcaddr); if (retval < 0) break; nrecorded += retval; soff += sizeof(struct in6_addr); - if (soff >= m->m_len) { - soff = soff - m->m_len; - m = m->m_next; - if (m == NULL) - break; - } } if (nrecorded > 0) { CTR1(KTR_MLD, @@ -1276,8 +1274,8 @@ mld_input(struct mbuf *m, int off, int icmp6len) if (mld_v1_input_query(ifp, ip6, mld) != 0) return (0); } else if (icmp6len >= sizeof(struct mldv2_query)) { - if (mld_v2_input_query(ifp, ip6, m, off, - icmp6len) != 0) + if (mld_v2_input_query(ifp, ip6, m, + (struct mldv2_query *)mld, off, icmp6len) != 0) return (0); } break; Modified: releng/12.0/sys/netinet6/mld6.c ============================================================================== --- releng/12.0/sys/netinet6/mld6.c Tue Aug 6 17:09:47 2019 (r350643) +++ releng/12.0/sys/netinet6/mld6.c Tue Aug 6 17:11:17 2019 (r350644) @@ -139,14 +139,15 @@ static int mld_v2_enqueue_group_record(struct mbufq *, struct in6_multi *, const int, const int, const int, const int); static int mld_v2_input_query(struct ifnet *, const struct ip6_hdr *, - struct mbuf *, const int, const int); + struct mbuf *, struct mldv2_query *, const int, const int); static int mld_v2_merge_state_changes(struct in6_multi *, struct mbufq *); static void mld_v2_process_group_timers(struct in6_multi_head *, struct mbufq *, struct mbufq *, struct in6_multi *, const int); static int mld_v2_process_group_query(struct in6_multi *, - struct mld_ifsoftc *mli, int, struct mbuf *, const int); + struct mld_ifsoftc *mli, int, struct mbuf *, + struct mldv2_query *, const int); static int sysctl_mld_gsr(SYSCTL_HANDLER_ARGS); static int sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS); @@ -797,16 +798,16 @@ mld_v1_update_group(struct in6_multi *inm, const int t * Process a received MLDv2 general, group-specific or * group-and-source-specific query. * - * Assumes that the query header has been pulled up to sizeof(mldv2_query). + * Assumes that mld points to a struct mldv2_query which is stored in + * contiguous memory. * * Return 0 if successful, otherwise an appropriate error code is returned. */ static int mld_v2_input_query(struct ifnet *ifp, const struct ip6_hdr *ip6, - struct mbuf *m, const int off, const int icmp6len) + struct mbuf *m, struct mldv2_query *mld, const int off, const int icmp6len) { struct mld_ifsoftc *mli; - struct mldv2_query *mld; struct in6_multi *inm; uint32_t maxdelay, nsrc, qqi; int is_general_query; @@ -831,8 +832,6 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 CTR2(KTR_MLD, "input v2 query on ifp %p(%s)", ifp, if_name(ifp)); - mld = (struct mldv2_query *)(mtod(m, uint8_t *) + off); - maxdelay = ntohs(mld->mld_maxdelay); /* in 1/10ths of a second */ if (maxdelay >= 32768) { maxdelay = (MLD_MRC_MANT(maxdelay) | 0x1000) << @@ -957,7 +956,7 @@ mld_v2_input_query(struct ifnet *ifp, const struct ip6 * group-specific or group-and-source query. */ if (mli->mli_v2_timer == 0 || mli->mli_v2_timer >= timer) - mld_v2_process_group_query(inm, mli, timer, m, off); + mld_v2_process_group_query(inm, mli, timer, m, mld, off); /* XXX Clear embedded scope ID as userland won't expect it. */ in6_clearscope(&mld->mld_addr); @@ -978,9 +977,8 @@ out_locked: */ static int mld_v2_process_group_query(struct in6_multi *inm, struct mld_ifsoftc *mli, - int timer, struct mbuf *m0, const int off) + int timer, struct mbuf *m0, struct mldv2_query *mld, const int off) { - struct mldv2_query *mld; int retval; uint16_t nsrc; @@ -988,7 +986,6 @@ mld_v2_process_group_query(struct in6_multi *inm, stru MLD_LOCK_ASSERT(); retval = 0; - mld = (struct mldv2_query *)(mtod(m0, uint8_t *) + off); switch (inm->in6m_state) { case MLD_NOT_MEMBER: @@ -1008,6 +1005,15 @@ mld_v2_process_group_query(struct in6_multi *inm, stru nsrc = ntohs(mld->mld_numsrc); + /* Length should be checked by calling function. */ + KASSERT((m0->m_flags & M_PKTHDR) == 0 || + m0->m_pkthdr.len >= off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), + ("mldv2 packet is too short: (%d bytes < %zd bytes, m=%p)", + m0->m_pkthdr.len, off + sizeof(struct mldv2_query) + + nsrc * sizeof(struct in6_addr), m0)); + + /* * Deal with group-specific queries upfront. * If any group query is already pending, purge any recorded @@ -1049,28 +1055,20 @@ mld_v2_process_group_query(struct in6_multi *inm, stru * report for those sources. */ if (inm->in6m_nsrc > 0) { - struct mbuf *m; - uint8_t *sp; + struct in6_addr srcaddr; int i, nrecorded; int soff; - m = m0; soff = off + sizeof(struct mldv2_query); nrecorded = 0; for (i = 0; i < nsrc; i++) { - sp = mtod(m, uint8_t *) + soff; - retval = in6m_record_source(inm, - (const struct in6_addr *)sp); + m_copydata(m0, soff, sizeof(struct in6_addr), + (caddr_t)&srcaddr); + retval = in6m_record_source(inm, &srcaddr); if (retval < 0) break; nrecorded += retval; soff += sizeof(struct in6_addr); - if (soff >= m->m_len) { - soff = soff - m->m_len; - m = m->m_next; - if (m == NULL) - break; - } } if (nrecorded > 0) { CTR1(KTR_MLD, @@ -1279,8 +1277,8 @@ mld_input(struct mbuf *m, int off, int icmp6len) if (mld_v1_input_query(ifp, ip6, mld) != 0) return (0); } else if (icmp6len >= sizeof(struct mldv2_query)) { - if (mld_v2_input_query(ifp, ip6, m, off, - icmp6len) != 0) + if (mld_v2_input_query(ifp, ip6, m, + (struct mldv2_query *)mld, off, icmp6len) != 0) return (0); } break;