Subject: Re: rt_tables broken
To: None <current-users@NetBSD.org>
From: David Young <dyoung@pobox.com>
List: current-users
Date: 08/05/2007 15:07:27
--5/uDoXvLw7AC5HRs
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sat, Aug 04, 2007 at 03:25:13AM -0500, David Young wrote:
> On Sat, Aug 04, 2007 at 03:47:12PM +0900, KIYOHARA Takashi wrote:
> > Hi! dyoung,
> > 
> > 
> > The rt_tables was broken since 07/19/2007 20:49:00 perhaps.  X-<
> > 
> >   http://mail-index.netbsd.org/source-changes/2007/07/19/0032.html
> 
> The bug is not new since 19 July 2007.  The kernel has been sloppy
> about copying link-layer addresses for a long time.  fwip(4) suffers
> the buffer overflow because its link-layer addresses are the longest.
> I have attached a patch that shows an approach to a fix for the bugs.
> I leave it to you to make the patch compile and run. :-)

Here is a new patch, also untested.  Lengthening sockaddr_dl is still
left as an exercise for the reader. :-)

Dave

-- 
David Young             OJC Technologies
dyoung@ojctech.com      Urbana, IL * (217) 278-3933 ext 24

--5/uDoXvLw7AC5HRs
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="dl_setaddr.patch2"

Index: sys/net/if_dl.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_dl.h,v
retrieving revision 1.18
diff -p -u -u -p -r1.18 if_dl.h
--- sys/net/if_dl.h	11 Dec 2005 23:05:24 -0000	1.18
+++ sys/net/if_dl.h	5 Aug 2007 19:42:46 -0000
@@ -78,7 +78,12 @@ struct sockaddr_dl {
 #define LLADDR(s) ((char *)((s)->sdl_data + (s)->sdl_nlen))
 #define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
 
-#ifndef _KERNEL
+#ifdef _KERNEL
+uint8_t sockaddr_dl_measure(uint8_t, uint8_t);
+void sockaddr_dl_init(struct sockaddr_dl *, uint16_t, uint8_t,
+    const void *, uint8_t, const void *, uint8_t);
+struct sockaddr_dl *sockaddr_dl_setaddr(struct sockaddr_dl *, void *, uint8_t);
+#else
 
 #include <sys/cdefs.h>
 
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.194
diff -p -u -u -p -r1.194 if.c
--- sys/net/if.c	19 Jul 2007 20:48:52 -0000	1.194
+++ sys/net/if.c	5 Aug 2007 19:42:46 -0000
@@ -271,8 +271,8 @@ void
 if_alloc_sadl(struct ifnet *ifp)
 {
 	unsigned socksize, ifasize;
-	int namelen, masklen;
-	struct sockaddr_dl *sdl;
+	int addrlen, namelen;
+	struct sockaddr_dl *mask, *sdl;
 	struct ifaddr *ifa;
 
 	/*
@@ -284,23 +284,23 @@ if_alloc_sadl(struct ifnet *ifp)
 		if_free_sadl(ifp);
 
 	namelen = strlen(ifp->if_xname);
-	masklen = offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
-	socksize = masklen + ifp->if_addrlen;
-#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(long) - 1)))
-	if (socksize < sizeof(*sdl))
-		socksize = sizeof(*sdl);
-	socksize = ROUNDUP(socksize);
+	addrlen = ifp->if_addrlen;
+	socksize = roundup(
+	    MAX(sizeof(*sdl),
+	    sockaddr_dl_measure(namelen, addrlen)), sizeof(long));
 	ifasize = sizeof(*ifa) + 2 * socksize;
 	ifa = (struct ifaddr *)malloc(ifasize, M_IFADDR, M_WAITOK);
-	memset((void *)ifa, 0, ifasize);
+	memset(ifa, 0, ifasize);
+
 	sdl = (struct sockaddr_dl *)(ifa + 1);
-	sdl->sdl_len = socksize;
-	sdl->sdl_family = AF_LINK;
-	memcpy(sdl->sdl_data, ifp->if_xname, namelen);
-	sdl->sdl_nlen = namelen;
-	sdl->sdl_alen = ifp->if_addrlen;
-	sdl->sdl_index = ifp->if_index;
-	sdl->sdl_type = ifp->if_type;
+	mask = (struct sockaddr_dl *)(socksize + (char *)sdl);
+
+	sockaddr_dl_init(sdl, ifp->if_index, ifp->if_type,
+	    ifp->if_xname, namelen, NULL, addrlen);
+	mask->sdl_len = sockaddr_dl_measure(namelen, 0);
+	while (namelen != 0)
+		mask->sdl_data[--namelen] = 0xff;
+
 	ifnet_addrs[ifp->if_index] = ifa;
 	IFAREF(ifa);
 	ifa->ifa_ifp = ifp;
@@ -309,11 +309,7 @@ if_alloc_sadl(struct ifnet *ifp)
 	IFAREF(ifa);
 	ifa->ifa_addr = (struct sockaddr *)sdl;
 	ifp->if_sadl = sdl;
-	sdl = (struct sockaddr_dl *)(socksize + (char *)sdl);
-	ifa->ifa_netmask = (struct sockaddr *)sdl;
-	sdl->sdl_len = masklen;
-	while (namelen != 0)
-		sdl->sdl_data[--namelen] = 0xff;
+	ifa->ifa_netmask = (struct sockaddr *)mask;
 }
 
 /*
Index: sys/net/link_proto.c
===================================================================
RCS file: /cvsroot/src/sys/net/link_proto.c,v
retrieving revision 1.1
diff -p -u -u -p -r1.1 link_proto.c
--- sys/net/link_proto.c	19 Jul 2007 20:48:52 -0000	1.1
+++ sys/net/link_proto.c	5 Aug 2007 19:42:46 -0000
@@ -110,6 +110,29 @@ submemcmp(const void *l, const void *r,
 	return 0;
 }
 
+uint8_t
+sockaddr_dl_measure(uint8_t namelen, uint8_t addrlen)
+{
+	return offsetof(struct sockaddr_dl, sdl_data[0]) + namelen + addrlen;
+}
+
+void
+sockaddr_dl_init(struct sockaddr_dl *sdl, uint16_t ifindex, uint8_t type,
+    const void *name, uint8_t namelen, const void *addr, uint8_t addrlen)
+{
+	sdl->sdl_family = AF_LINK;
+	sdl->sdl_len = sockaddr_dl_measure(namelen, addrlen);
+	KASSERT(sdl->sdl_len <= sizeof(*sdl));
+	sdl->sdl_index = ifindex;
+	sdl->sdl_type = type;
+	memset(&sdl->sdl_data[0], 0, sizeof(sdl->sdl_data));
+	memcpy(&sdl->sdl_data[0], name, namelen);
+	sdl->sdl_nlen = namelen;
+	if (addr != NULL)
+		memcpy(&sdl->sdl_data[sdl->sdl_nlen], addr, addrlen);
+	sdl->sdl_alen = addrlen;
+}
+
 static int
 sockaddr_dl_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
 {
@@ -158,3 +181,22 @@ sockaddr_dl_cmp(const struct sockaddr *s
 
 	return sdl1->sdl_len - sdl2->sdl_len;
 }
+
+struct sockaddr_dl *
+sockaddr_dl_setaddr(struct sockaddr_dl *sdl, void *addr, uint8_t addrlen)
+{
+	uint_fast8_t endofs;
+
+	endofs =
+	    offsetof(struct sockaddr_dl, sdl_data[sdl->sdl_nlen + addrlen]);
+
+	if (endofs > sizeof(struct sockaddr_dl)) {
+		printf("%s: no room for %" PRIu8 "-byte media address\n",
+		    __func__, addrlen);
+		sdl->sdl_alen = 0;
+		return NULL;
+	}
+	memcpy(&sdl->sdl_data[sdl->sdl_nlen], addr, addrlen);
+	sdl->sdl_alen = addrlen;
+	return sdl;
+}
Index: sys/netinet6/nd6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6.c,v
retrieving revision 1.117
diff -p -u -u -p -r1.117 nd6.c
--- sys/netinet6/nd6.c	19 Jul 2007 20:48:57 -0000	1.117
+++ sys/netinet6/nd6.c	5 Aug 2007 19:42:46 -0000
@@ -1314,8 +1314,9 @@ nd6_rtrequest(int req, struct rtentry *r
 			ln->ln_state = ND6_LLINFO_REACHABLE;
 			ln->ln_byhint = 0;
 			if (macp) {
-				bcopy(macp, LLADDR(SDL(gate)), ifp->if_addrlen);
-				SDL(gate)->sdl_alen = ifp->if_addrlen;
+				/* XXX check for error */
+				(void)sockaddr_dl_setaddr(SDL(gate), macp,
+				    ifp->if_addrlen);
 			}
 			if (nd6_useloopback) {
 				rt->rt_ifp = lo0ifp;	/* XXX */
@@ -1738,8 +1739,8 @@ fail:
 		 * Record source link-layer address
 		 * XXX is it dependent to ifp->if_type?
 		 */
-		sdl->sdl_alen = ifp->if_addrlen;
-		bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);
+		/* XXX check for error */
+		(void)sockaddr_dl_setaddr(sdl, lladdr, ifp->if_addrlen);
 	}
 
 	if (!is_newentry) {
@@ -2130,7 +2131,8 @@ nd6_storelladdr(const struct ifnet *ifp,
 			ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, lldst);
 			return 1;
 		case IFT_IEEE1394:
-			bcopy(ifp->if_broadcastaddr, lldst, ifp->if_addrlen);
+			memcpy(lldst, ifp->if_broadcastaddr,
+			    MIN(dstsize, ifp->if_addrlen));
 			return 1;
 		case IFT_ARCNET:
 			*lldst = 0;
Index: sys/netinet/if_arp.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/if_arp.c,v
retrieving revision 1.125
diff -p -u -u -p -r1.125 if_arp.c
--- sys/netinet/if_arp.c	19 Jul 2007 20:48:53 -0000	1.125
+++ sys/netinet/if_arp.c	5 Aug 2007 19:42:47 -0000
@@ -575,9 +596,9 @@ arp_rtrequest(int req, struct rtentry *r
 			 * interface.
 			 */
 			rt->rt_expire = 0;
-			Bcopy(LLADDR(rt->rt_ifp->if_sadl),
-			    LLADDR(SDL(gate)),
-			    SDL(gate)->sdl_alen = rt->rt_ifp->if_addrlen);
+			(void)sockaddr_dl_setaddr(SDL(gate),
+			    LLADDR(rt->rt_ifp->if_sadl),
+			    rt->rt_ifp->if_addrlen);
 			if (useloopback)
 				rt->rt_ifp = lo0ifp;
 			/*
@@ -1069,7 +1087,7 @@ in_arpinput(struct mbuf *m)
 			}
 		}
 #endif /* NTOKEN > 0 */
-		memcpy(LLADDR(sdl), ar_sha(ah), sdl->sdl_alen = ah->ar_hln);
+		(void)sockaddr_dl_setaddr(sdl, ar_sha(ah), ah->ar_hln);
 		if (rt->rt_expire)
 			rt->rt_expire = time_second + arpt_keep;
 		rt->rt_flags &= ~RTF_REJECT;
Index: sys/kern/uipc_domain.c
===================================================================
RCS file: /cvsroot/src/sys/kern/uipc_domain.c,v
retrieving revision 1.67
diff -p -u -u -p -r1.67 uipc_domain.c
--- sys/kern/uipc_domain.c	19 Jul 2007 20:48:51 -0000	1.67
+++ sys/kern/uipc_domain.c	5 Aug 2007 20:06:54 -0000
@@ -204,11 +204,28 @@ sockaddr_alloc(sa_family_t af, int flags
 	return sa;
 }
 
+static void
+sockaddr_fixlen(struct sockaddr *dst, uint8_t deslen)
+{
+	struct domain *dom;
+
+	if ((dom = pffinddomain(dst->sa_family)) == NULL)
+		panic("%s: unknown domain %d", __func__, dst->sa_family);
+	if (dom->dom_sa_len < deslen)
+		panic("%s: source too long, %d bytes", __func__, deslen);
+	dst->sa_len = dom->dom_sa_len;
+}
+
 struct sockaddr *
 sockaddr_copy(struct sockaddr *dst, const struct sockaddr *src)
 {
 	KASSERT(dst->sa_family == src->sa_family);
+
+	if (__predict_false(dst->sa_len < src->sa_len))
+		sockaddr_fixlen(dst, src->sa_len);
+
 	memcpy(dst, src, src->sa_len);
+
 	return dst;
 }
 

--5/uDoXvLw7AC5HRs--