⬢ DragonFlyBSD Kernel Audit
DF-3319 / fix.diff
← back to finding ↓ download raw
--- 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;