Re: 5.3-RELEASE TODO

From: Brooks Davis <brooks_at_one-eyed-alien.net>
Date: Fri, 17 Sep 2004 09:25:35 -0700
On Fri, Sep 17, 2004 at 01:41:16AM -0600, Scott Long wrote:
>  |--------------------+-----------+-------------+-------------------------|
>  |                    |           |             |The ifconf() ioctl for   |
>  |                    |           |             |listing network          |
>  |                    |           |             |interfaces performs a    |
>  |                    |           |             |copyout() while holding  |
>  |ifconf() sleep      |           |             |the global ifnet list    |
>  |warning             |In progress|Robert Watson|mutex. This generates a  |
>  |                    |           |             |witness warning in the   |
>  |                    |           |             |event that copyout()     |
>  |                    |           |             |generates a page fault,  |
>  |                    |           |             |and risks more serious   |
>  |                    |           |             |problems.                |
>  +------------------------------------------------------------------------+

I've got a patch for this one.  If someone will review it, I will commit
it to HEAD for further testing.

-- Brooks

http://perforce.freebsd.org/chv.cgi?CH=61588

Change 61588 by brooks_at_brooks_minya on 2004/09/16 05:07:10

	Fix a potential LOR in ifconf caused by using copyout while
	holding a lock.
	
	Reported by:	rwatson

Affected files ...

.. //depot/user/brooks/cleanup/sys/net/if.c#38 edit

Differences ...

==== //depot/user/brooks/cleanup/sys/net/if.c#38 (text+ko) ====

_at__at_ -123,6 +123,7 _at__at_
 SYSINIT(interfaces, SI_SUB_INIT_IF, SI_ORDER_FIRST, if_init, NULL)
 SYSINIT(interface_check, SI_SUB_PROTO_IF, SI_ORDER_FIRST, if_check, NULL)
 
+MALLOC_DEFINE(M_IFCONF, "ifconf", "interface configuration info");
 MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
 MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
 
_at__at_ -1484,10 +1485,27 _at__at_
 	struct ifconf *ifc = (struct ifconf *)data;
 	struct ifnet *ifp;
 	struct ifaddr *ifa;
-	struct ifreq ifr, *ifrp;
+	struct ifreq ifr, *ifrp, *ifrbuf = NULL;
 	int space = ifc->ifc_len, error = 0;
+	size_t buflen = 0;
 
-	ifrp = ifc->ifc_req;
+	/*
+	 * Because it is not safe to do a copyout while a lock it held,
+	 * we need to avoid doing a copyout while walking the interface
+	 * list.  The easiest way to do that is to stage all the data
+	 * into a single buffer rather then doing many copyouts.
+	 * Unfortunatly, we can't just trust the users buffer length
+	 * because that might cause use to allocate too much kernel
+	 * memory.  Thus we need to bound the memory by allocate by the 
+	 * actual space needed.  In order to do that with the minimum
+	 * duplication of code, we make two passes through the loop over
+	 * interfaces and addresses.  In the first pass, we have no
+	 * buffer and we count how much space we needed and malloc it
+	 * after releasing the lock.  Then we go through again and
+	 * actually fill the buffer followed by copying it out.
+	 */
+again:
+	ifrp = ifrbuf;
 	IFNET_RLOCK();		/* could sleep XXX */
 	TAILQ_FOREACH(ifp, &ifnet, if_link) {
 		int addrs;
_at__at_ -1516,47 +1534,68 _at__at_
 					 (struct osockaddr *)&ifr.ifr_addr;
 				ifr.ifr_addr = *sa;
 				osa->sa_family = sa->sa_family;
-				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
-						sizeof (ifr));
-				ifrp++;
+				if (ifrp == NULL) {
+					buflen += sizeof(ifr);
+				} else {
+					bcopy(&ifr, ifrp, sizeof(ifr));
+					ifrp++;
+				}
+					
 			} else
 #endif
 			if (sa->sa_len <= sizeof(*sa)) {
 				ifr.ifr_addr = *sa;
-				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
-						sizeof (ifr));
-				ifrp++;
+				if (ifrp == NULL) {
+					buflen += sizeof(struct ifreq);
+				} else {
+					bcopy(&ifr, ifrp, sizeof(ifr));
+					ifrp++;
+				}
 			} else {
 				if (space < sizeof (ifr) + sa->sa_len -
 					    sizeof(*sa))
 					break;
 				space -= sa->sa_len - sizeof(*sa);
-				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
-						sizeof (ifr.ifr_name));
-				if (error == 0)
-				    error = copyout((caddr_t)sa,
-				      (caddr_t)&ifrp->ifr_addr, sa->sa_len);
-				ifrp = (struct ifreq *)
-					(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
+				if (ifrp == NULL) {
+					buflen +=
+					    offsetof(struct ifreq, ifr_addr) +
+					    sa->sa_len;
+				} else {
+					bcopy(&ifr, ifrp,
+					    sizeof(ifr.ifr_name));
+					bcopy(sa, &ifrp->ifr_addr, sa->sa_len);
+					ifrp = (struct ifreq *)
+					    (sa->sa_len +
+					    (caddr_t)&ifrp->ifr_addr);
+				}
 			}
-			if (error)
-				break;
 			space -= sizeof (ifr);
 		}
-		if (error)
-			break;
-		if (!addrs) {
+		if (addrs == 0) {
 			bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr));
-			error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
-			    sizeof (ifr));
-			if (error)
-				break;
+			if (ifrp == NULL) {
+				buflen += sizeof(struct ifreq);
+			} else {
+				bcopy(&ifr, ifrp, sizeof(ifr));
+				ifrp++;
+			}
 			space -= sizeof (ifr);
-			ifrp++;
 		}
 	}
 	IFNET_RUNLOCK();
-	ifc->ifc_len -= space;
+
+	if (error != 0)
+		return (error);
+
+	if (ifrp == NULL) {
+		ifrbuf = malloc(buflen, M_IFCONF, M_ZERO | M_NOWAIT);
+		space = buflen;
+		goto again;
+	}
+
+	ifc->ifc_len = buflen - space;
+	error = copyout(ifrbuf, ifc->ifc_req, ifc->ifc_len);
+	free(ifrbuf, M_IFCONF);
 	return (error);
 }
 

-- 
Any statement of the form "X is the one, true Y" is FALSE.
PGP fingerprint 655D 519C 26A7 82E7 2529  9BF0 5D8E 8BE9 F238 1AD4

Received on Fri Sep 17 2004 - 14:24:10 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:12 UTC