Skip site navigation (1)Skip section navigation (2)
Date:      11 Mar 2000 07:14:15 -0000
From:      patrick@mindstep.com
To:        freefall-gnats@mindstep.com
Subject:   kern/17311: bug in the code handling ioctl SIOCGIFCONF
Message-ID:  <20000311071415.26789.qmail@modemcable127.61-201-24.mtl.mc.videotron.net>

next in thread | raw e-mail | index | archive | help

>Number:         17311
>Category:       kern
>Synopsis:       bug in the code handling ioctl SIOCGIFCONF
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Mar 10 23:20:01 PST 2000
>Closed-Date:
>Last-Modified:
>Originator:     Patrick Bihan-Faou
>Release:        FreeBSD 3.4-STABLE i386
>Organization:
MindStep Corporation
>Environment:

FreeBSD 3.4-STABLE and FreeBSD 4.0-CURRENT

>Description:

When invoked with a small buffer size, the ioctl SIOCGIFCONF may return
more information than can fit in the provided buffer.

This problem has been partially fixed in FreeBSD 4.0 (if.c rev 1.85). 
It does not write outside the boundary of the provided buffer but the 
returned buffer size is incorrect. This in turn can lead to problems 
in the calling code.

This only happens if the system has interfaces for which sa->sa_len is greater
than sizeof(*sa), which is the case with most non-IPv4 addresses...

This problem has been noted in PR kern/12996 and PR kern/14457 without
a good fix yet.


>How-To-Repeat:

The following piece of code will show the problem.



#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/sockio.h>

#define VERBOSE_CHECK
check55(char *start,char *end)
{
	int	startoff=-1,endoff=0;
	int	off=0,c=0;

#ifdef VERBOSE_CHECK
	printf("%03d\t",off);
#endif
	for(;start<end;start++,off++)
	{
		if(*start != 0x55)
		{
			if(startoff<0)
			{
				startoff=off;
			}
			endoff=off;
		}
#ifdef VERBOSE_CHECK
		if(++c>=33)
		{
			printf("\n%03d\t",off);
			c=1;
		}
		printf("%02x ",*(unsigned char*)start);
#endif
	}
	printf("\n");
	if(startoff>=0)
	{
		printf("	** buffer changed from %d to %d => %d bytes modified **\n",startoff,endoff, endoff - startoff + 1);
	}
}

main()
{
  struct ifconf ifc;
  char *x;
  struct ifreq *ifr;
  struct sockaddr_in *sin;
  int len,ret;
  int s;
  char buf[1024];
#define END_TEST	300
 
  if ((s = socket(AF_INET,SOCK_STREAM,0)) == -1) return -1;
 
  for (len=1;len<=END_TEST;len++) {
    ifc.ifc_buf = buf;
    ifc.ifc_len = len;
	memset(buf,0x55,sizeof(buf));
	printf("\n");
	printf("[Try with len=%d]\n",len);
    if ((ret=ioctl(s,SIOCGIFCONF,&ifc)) < 0)
	{
		printf(" => ioctl failed (returned %d, errno=%d)\n",ret,errno);
	}
	printf(" => ioctl succeeded, pretends it wrote %d bytes\n",ifc.ifc_len);
	printf("\n");
	check55(buf,buf+sizeof(buf));
	printf("\n");
  }
  return 0;
}

>Fix:
	


Fix to apply against the HEAD of the CVS

Index: if.c
===================================================================
RCS file: /cvs/freebsd/src/sys/net/if.c,v
retrieving revision 1.85
diff -u -r1.85 if.c
--- if.c	2000/02/28 19:30:25	1.85
+++ if.c	2000/03/10 23:29:50
@@ -1086,11 +1086,9 @@
 						sizeof (ifr));
 				ifrp++;
 			} else {
-				if (space < sa->sa_len - sizeof(*sa))
+				if (space < sizeof(ifr) + sa->sa_len - sizeof(*sa))
 					break;
 				space -= sa->sa_len - sizeof(*sa);
-				if (space < sizeof (ifr))
-					break;
 				error = copyout((caddr_t)&ifr, (caddr_t)ifrp,
 						sizeof (ifr.ifr_name));
 				if (error == 0)






Fix to apply against the RELENG_3 branch of the CVS

Index: if.c
===================================================================
RCS file: /cvs/freebsd/src/sys/net/if.c,v
retrieving revision 1.64.2.3
diff -u -r1.64.2.3 if.c
--- if.c	2000/01/27 16:54:57	1.64.2.3
+++ if.c	2000/03/10 23:29:31
@@ -861,9 +861,9 @@
 						sizeof (ifr));
 				ifrp++;
 			} else {
-				space -= sa->sa_len - sizeof(*sa);
-				if (space < sizeof (ifr))
+				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)


This PR should close PR kern/12996 and kern/14457



Patrick Bihan-Faou, Herve Masson
MindStep Corporation


>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20000311071415.26789.qmail>