--- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -488,6 +488,12 @@ case SIOCGIFALIAS: if (ifp == NULL) return (EADDRNOTAVAIL); + if (cmd == SIOCAIFADDR && + (ifra->ifra_addr.sin_len > sizeof(struct sockaddr_in) || + ifra->ifra_mask.sin_len > sizeof(struct sockaddr_in) || + ifra->ifra_dstaddr.sin_len > sizeof(struct sockaddr_in) || + ifra->ifra_broadaddr.sin_len > sizeof(struct sockaddr_in))) + return (EINVAL); if (ifra->ifra_addr.sin_family == AF_INET) { while (ia != NULL) { if (ia->ia_ifp == ifp && @@ -598,6 +604,9 @@ case SIOCSIFDSTADDR: KKASSERT(ifp->if_flags & IFF_POINTOPOINT); + if (((const struct sockaddr_in *)&ifr->ifr_dstaddr)->sin_len > + sizeof(struct sockaddr_in)) + return (EINVAL); oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *(struct sockaddr_in *)&ifr->ifr_dstaddr; if (ifp->if_ioctl != NULL) { @@ -622,6 +631,9 @@ case SIOCSIFBRDADDR: if ((ifp->if_flags & IFF_BROADCAST) == 0) return (EINVAL); + if (((const struct sockaddr_in *)&ifr->ifr_broadaddr)->sin_len > + sizeof(struct sockaddr_in)) + return (EINVAL); ia->ia_broadaddr = *(struct sockaddr_in *)&ifr->ifr_broadaddr; return (0); @@ -875,6 +887,7 @@ struct in_addr mask, candidate, match; struct sockaddr_in *sin; int cmp; + u_int alen, dlen, mlen; bzero(&mask, sizeof mask); if (iflr->flags & IFLR_PREFIX) { @@ -908,12 +921,12 @@ TAILQ_FOREACH(ifac, &ifp->if_addrheads[mycpuid], ifa_link) { struct ifaddr *ifa = ifac->ifa; - if (ifa->ifa_addr->sa_family != AF_INET6) + if (ifa->ifa_addr->sa_family != AF_INET) continue; if (!cmp) break; candidate.s_addr = - ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr; + ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; candidate.s_addr &= mask.s_addr; if (candidate.s_addr == match.s_addr) break; @@ -922,13 +935,27 @@ return EADDRNOTAVAIL; ia = (struct in_ifaddr *)(ifac->ifa); + /* + * Never trust a sin_len stored in a live in_ifaddr as a + * copy length: they are persisted verbatim from user + * ioctls (and a type-confused ifaddr yields garbage). + */ + alen = ia->ia_addr.sin_len; + dlen = ia->ia_dstaddr.sin_len; + mlen = ia->ia_sockmask.sin_len; + if (alen > sizeof(struct sockaddr_in)) + alen = sizeof(struct sockaddr_in); + if (dlen > sizeof(struct sockaddr_in)) + dlen = sizeof(struct sockaddr_in); + if (mlen > sizeof(struct sockaddr_in)) + mlen = sizeof(struct sockaddr_in); + if (cmd == SIOCGLIFADDR) { /* fill in the if_laddrreq structure */ - bcopy(&ia->ia_addr, &iflr->addr, ia->ia_addr.sin_len); + bcopy(&ia->ia_addr, &iflr->addr, alen); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { - bcopy(&ia->ia_dstaddr, &iflr->dstaddr, - ia->ia_dstaddr.sin_len); + bcopy(&ia->ia_dstaddr, &iflr->dstaddr, dlen); } else bzero(&iflr->dstaddr, sizeof iflr->dstaddr); @@ -946,14 +973,12 @@ bcopy(iflr->iflr_name, ifra.ifra_name, sizeof ifra.ifra_name); - bcopy(&ia->ia_addr, &ifra.ifra_addr, - ia->ia_addr.sin_len); + bcopy(&ia->ia_addr, &ifra.ifra_addr, alen); if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { bcopy(&ia->ia_dstaddr, &ifra.ifra_dstaddr, - ia->ia_dstaddr.sin_len); + dlen); } - bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, - ia->ia_sockmask.sin_len); + bcopy(&ia->ia_sockmask, &ifra.ifra_dstaddr, mlen); return in_control_internal(SIOCDIFADDR, (caddr_t)&ifra, ifp, td); @@ -987,6 +1012,10 @@ int flags = RTF_UP, error = 0; int was_hash = 0; + /* ia_addr keeps sin_len verbatim; never persist an oversized one */ + if (sin->sin_len > sizeof(struct sockaddr_in)) + return (EINVAL); + ifac = &ia->ia_ifa.ifa_containers[mycpuid]; oldaddr = ia->ia_addr;