From kargl@troutmask.apl.washington.edu  Mon Dec 13 12:51:18 1999
Return-Path: <kargl@troutmask.apl.washington.edu>
Received: from troutmask.apl.washington.edu (troutmask.apl.washington.edu [128.95.76.54])
	by hub.freebsd.org (Postfix) with ESMTP id 994FF15266
	for <FreeBSD-gnats-submit@freebsd.org>; Mon, 13 Dec 1999 12:51:09 -0800 (PST)
	(envelope-from kargl@troutmask.apl.washington.edu)
Received: (from kargl@localhost)
	by troutmask.apl.washington.edu (8.9.3/8.9.3) id MAA89742;
	Mon, 13 Dec 1999 12:50:24 -0800 (PST)
	(envelope-from kargl)
Message-Id: <199912132050.MAA89742@troutmask.apl.washington.edu>
Date: Mon, 13 Dec 1999 12:50:24 -0800 (PST)
From: "Steven G. Kargl" <kargl@troutmask.apl.washington.edu>
Reply-To: kargl@troutmask.apl.washington.edu
To: FreeBSD-gnats-submit@freebsd.org
Subject: fsplit buffer overflow fixes
X-Send-Pr-Version: 3.2

>Number:         15471
>Category:       bin
>Synopsis:       Fix several buffer overflows
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kris
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Dec 13 13:00:02 PST 1999
>Closed-Date:    Tue Feb 6 09:50:16 PST 2001
>Last-Modified:  Tue Feb 06 09:56:38 PST 2001
>Originator:     Steven G. Kargl
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
apl/uw
>Environment:

All version of FreeBSD.

>Description:

fsplit(1) has several buffer overflow problems.  These overflows 
could be exploited by a user on a system to cause problems (either
to breach security or panic a system).

>How-To-Repeat:

There are at least three ways to over flow buffers:

1. fsplit -e arg1 ... -e arg100 -e START_OF_OVERFLOW prog.f

2. fsplit -e arg_containing_more_1000_chars prog.f

3. fsplit prog.f

    where prog.f contains a Fortran subprogram with a name containing
    more than 20 chars:
  
       program ThisNameIsTooLongForTheBuffer
       end

>Fix:

The attached patch does:

* Remove the custom command line parser.  Use getopt(3), instead.
  Fixes 1 and 2 above.

* Use dynamic memory allocation to remove static buffers.
  Fixes 1 and 2 above.

* Keep track of characters copied from subprogram names in scan_name().
  Fixes 3 above.

* Give fsplit a "return 0;" statement

* Include <stdlib.h> to properly prototype exit(3).

* Whitespace clean up

* Update man page.  fsplit(1) works with Fortran 77 and older code.


diff -u -r /usr/src/usr.bin/fsplit/fsplit.1 fsplit/fsplit.1
--- /usr/src/usr.bin/fsplit/fsplit.1	Mon Aug 30 08:00:49 1999
+++ fsplit/fsplit.1	Mon Dec 13 12:25:49 1999
@@ -39,7 +39,7 @@
 .Os BSD 4.2
 .Sh NAME
 .Nm fsplit
-.Nd split a multi-routine Fortran file into individual files
+.Nd split a multi-routine Fortran 77 file into individual files
 .Sh SYNOPSIS
 .Nm fsplit
 .Op Fl e Ar efile
@@ -47,8 +47,8 @@
 .Op Ar file
 .Sh DESCRIPTION
 .Nm Fsplit
-takes as input either a file or standard input containing Fortran source code.
-It attempts to split the input into separate routine files of the
+takes as input either a file or standard input containing Fortran 77 source
+code.  It attempts to split the input into separate routine files of the
 form
 .Ar name.f ,
 where
@@ -104,3 +104,7 @@
 .Fl e
 for unnamed main programs and block data subprograms since you must
 predict the created file name.
+.Pp
+.Nm
+can be used with Fortran 77 and older source code.  It understands neither
+Fortran 90/95 syntax nor free form source files.
diff -u -r /usr/src/usr.bin/fsplit/fsplit.c fsplit/fsplit.c
--- /usr/src/usr.bin/fsplit/fsplit.c	Tue Sep  7 11:59:34 1999
+++ fsplit/fsplit.c	Mon Dec 13 12:14:36 1999
@@ -51,6 +51,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -90,11 +91,9 @@
 
 #define TRUE 1
 #define FALSE 0
-int	extr = FALSE,
-	extrknt = -1,
-	extrfnd[100];
-char	extrbuf[1000],
-	*extrnames[100];
+
+int extr = FALSE, extrknt = -1, *extrfnd;
+char **extrnames;
 struct stat sbuf;
 
 #define trim(p)	while (*p == ' ' || *p == '\t') p++
@@ -103,101 +102,119 @@
 void  get_name __P((char *, int));
 char *functs __P((char *));
 int   lend __P((void));
-int   lname __P((char *));
+int   lname __P((char *, int));
 char *look __P((char *, char *));
 int   saveit __P((char *));
-int   scan_name __P((char *, char *));
+int   scan_name __P((char *, char *, int));
 char *skiplab __P((char *));
 static void usage __P((void));
 
 int
 main(argc, argv)
+int argc;
 char **argv;
 {
+	extern int optind;
+	extern char *optarg;
+
 	register FILE *ofp;	/* output file */
 	register int rv;	/* 1 if got card in output file, 0 otherwise */
 	register char *ptr;
 	int nflag,		/* 1 if got name of subprog., 0 otherwise */
 		retval,
 		i;
-	char name[20],
-		*extrptr = extrbuf;
+	char name[20];
 
-	/*  scan -e options */
-	while ( argc > 1  && argv[1][0] == '-' && argv[1][1] == 'e') {
+	if (argc > 2) {
 		extr = TRUE;
-		ptr = argv[1] + 2;
-		if(!*ptr) {
-			argc--;
-			argv++;
-			if(argc <= 1)
+
+		extrfnd = (int *) malloc(argc * sizeof(int));
+		if (extrfnd == NULL)
+			errx(1, NULL);
+
+		extrnames = (char **) malloc(argc * sizeof(char *));
+		if (extrnames == NULL)
+			errx(1, NULL);
+
+		while ((i = getopt(argc, argv, "e:")) != -1) {
+			switch (i) {
+			case 'e':
+				extrknt++;
+				extrfnd[extrknt] = FALSE;
+				extrnames[extrknt] = optarg;
+				break;
+			default:
 				usage();
-			ptr = argv[1];
+			}
 		}
-		extrknt = extrknt + 1;
-		extrnames[extrknt] = extrptr;
-		extrfnd[extrknt] = FALSE;
-		while(*ptr) *extrptr++ = *ptr++;
-		*extrptr++ = 0;
+
+		argc -= optind;
+		argv += optind;
+	} else {
 		argc--;
 		argv++;
 	}
 
-	if (argc > 2)
+	if (argc > 1)
 		usage();
-	else if (argc == 2) {
-		if ((ifp = fopen(argv[1], "r")) == NULL)
+	else if (argc == 1) {
+		if ((ifp = fopen(*argv, "r")) == NULL)
 			errx(1, "cannot open %s", argv[1]);
 	}
 	else
 		ifp = stdin;
-    for(;;) {
-	/* look for a temp file that doesn't correspond to an existing file */
-	get_name(x, 3);
-	ofp = fopen(x, "w");
-	nflag = 0;
-	rv = 0;
-	while (getline() > 0) {
-		rv = 1;
-		fprintf(ofp, "%s", buf);
-		if (lend())		/* look for an 'end' statement */
-			break;
-		if (nflag == 0)		/* if no name yet, try and find one */
-			nflag = lname(name);
-	}
-	fclose(ofp);
-	if (rv == 0) {			/* no lines in file, forget the file */
-		unlink(x);
-		retval = 0;
-		for ( i = 0; i <= extrknt; i++ )
-			if(!extrfnd[i]) {
-				retval = 1;
-				warnx("%s not found", extrnames[i]);
-			}
-		exit( retval );
-	}
-	if (nflag) {			/* rename the file */
-		if(saveit(name)) {
-			if (stat(name, &sbuf) < 0 ) {
-				link(x, name);
-				unlink(x);
-				printf("%s\n", name);
+
+	for (;;) {
+		/* look for a temp file that doesn't correspond to an existing file */
+		get_name(x, 3);
+		ofp = fopen(x, "w");
+		if (ofp == NULL) 
+			errx(1, "can not open %s", x);
+		nflag = 0;
+		rv = 0;
+		while (getline() > 0) {
+			rv = 1;
+			fprintf(ofp, "%s", buf);
+			if (lend())		/* look for an 'end' statement */
+				break;
+			if (nflag == 0)		/* if no name yet, try and find one */
+				nflag = lname(name, 20);
+		}
+		fclose(ofp);
+		if (rv == 0) {			/* no lines in file, forget the file */
+			unlink(x);
+			retval = 0;
+			for ( i = 0; i <= extrknt; i++ )
+				if(!extrfnd[i]) {
+					retval = 1;
+					warnx("%s not found", extrnames[i]);
+				}
+			exit( retval );
+		}
+		if (nflag) {			/* rename the file */
+			if(saveit(name)) {
+				if (stat(name, &sbuf) < 0 ) {
+					link(x, name);
+					unlink(x);
+					printf("%s\n", name);
+					continue;
+				} else if (strcmp(name, x) == 0) {
+					printf("%s\n", x);
+					continue;
+				}
+				printf("%s already exists, put in %s\n", name, x);
 				continue;
-			} else if (strcmp(name, x) == 0) {
-				printf("%s\n", x);
+			} else
+				unlink(x);
 				continue;
-			}
-			printf("%s already exists, put in %s\n", name, x);
-			continue;
-		} else
+		}
+		if (!extr)
+			printf("%s\n", x);
+		else
 			unlink(x);
-			continue;
-	}
-	if(!extr)
-		printf("%s\n", x);
-	else
-		unlink(x);
-    }
+    	}
+
+	return 0;
 }
 
 static void
@@ -241,7 +258,7 @@
 				break;
 			*ptr = '0';
 		}
-		if(ptr < name + letters)
+		if (ptr < name + letters)
 			errx(1, "ran out of file names");
 	}
 }
@@ -293,18 +310,19 @@
 		name and put in arg string. invent name for unnamed
 		block datas and main programs.		*/
 int
-lname(s)
+lname(s, len)
 char *s;
+int len;
 {
 #	define LINESIZE 80
 	register char *ptr, *p;
 	char	line[LINESIZE], *iptr = line;
 
 	/* first check for comment cards */
-	if(buf[0] == 'c' || buf[0] == 'C' || buf[0] == '*') return(0);
+	if (buf[0] == 'c' || buf[0] == 'C' || buf[0] == '*') return(0);
 	ptr = buf;
-	while (*ptr == ' ' || *ptr == '\t') ptr++;
-	if(*ptr == '\n') return(0);
+	trim(ptr);
+	if (*ptr == '\n') return(0);
 
 
 	ptr = skiplab(buf);
@@ -324,38 +342,43 @@
 	if ((ptr = look(line, "subroutine")) != 0 ||
 	    (ptr = look(line, "function")) != 0 ||
 	    (ptr = functs(line)) != 0) {
-		if(scan_name(s, ptr)) return(1);
-		strcpy( s, x);
+		if (scan_name(s, ptr, len)) return(1);
+		strcpy(s, x);
 	} else if((ptr = look(line, "program")) != 0) {
-		if(scan_name(s, ptr)) return(1);
-		get_name( mainp, 4);
-		strcpy( s, mainp);
+		if(scan_name(s, ptr, len)) return(1);
+		get_name(mainp, 4);
+		strcpy(s, mainp);
 	} else if((ptr = look(line, "blockdata")) != 0) {
-		if(scan_name(s, ptr)) return(1);
-		get_name( blkp, 6);
-		strcpy( s, blkp);
+		if(scan_name(s, ptr, len)) return(1);
+		get_name(blkp, 6);
+		strcpy(s, blkp);
 	} else if((ptr = functs(line)) != 0) {
-		if(scan_name(s, ptr)) return(1);
-		strcpy( s, x);
+		if(scan_name(s, ptr, len)) return(1);
+		strcpy(s, x);
 	} else {
-		get_name( mainp, 4);
-		strcpy( s, mainp);
+		get_name(mainp, 4);
+		strcpy(s, mainp);
 	}
 	return(1);
 }
 
 int
-scan_name(s, ptr)
+scan_name(s, ptr, len)
 char *s, *ptr;
+int len;
 {
+	int cnt = 0;
 	char *sptr;
 
 	/* scan off the name */
 	trim(ptr);
 	sptr = s;
 	while (*ptr != '(' && *ptr != '\n') {
-		if (*ptr != ' ' && *ptr != '\t')
+		if (*ptr != ' ' && *ptr != '\t') {
 			*sptr++ = *ptr;
+			cnt++;
+			if (cnt == len - 3) break;
+		}
 		ptr++;
 	}
 

>Release-Note:
>Audit-Trail:

From: Sheldon Hearn <sheldonh@uunet.co.za>
To: kargl@troutmask.apl.washington.edu
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: bin/15471: fsplit buffer overflow fixes 
Date: Tue, 14 Dec 1999 14:54:59 +0200

 On Mon, 13 Dec 1999 12:50:24 PST, "Steven G. Kargl" wrote:
 
 > * Whitespace clean up
 
 Can you send the diff without the whitespace changes?  That's how it'd
 have to be committed in any case, with whitespace changes (which are
 discouraged) being committed seperately.
 
 Ciao,
 Sheldon.
 

From: "Steven G. Kargl" <kargl@troutmask.apl.washington.edu>
To: sheldonh@uunet.co.za (Sheldon Hearn)
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: bin/15471: fsplit buffer overflow fixes
Date: Tue, 14 Dec 1999 11:06:41 -0800 (PST)

 Once upon a time, Sheldon Hearn said:
 > 
 > 
 > On Mon, 13 Dec 1999 12:50:24 PST, "Steven G. Kargl" wrote:
 > 
 > > * Whitespace clean up
 > 
 > Can you send the diff without the whitespace changes?  That's how it'd
 > have to be committed in any case, with whitespace changes (which are
 > discouraged) being committed seperately.
 
 * Added "include <stdlib.h>" for exit(3) prototype
 * Use getopt(3) instead of custom command line parser
 * Use dynamic memory allocation to allevaite buffer overflows
 * Update man page
 * Declare argc
 * Give int main() a return value of 0
 
 --
 Steve
 
 
 diff -r -u /usr/src/usr.bin/fsplit/fsplit.1 fsplit/fsplit.1
 --- /usr/src/usr.bin/fsplit/fsplit.1	Mon Aug 30 08:00:49 1999
 +++ fsplit/fsplit.1	Tue Dec 14 10:55:53 1999
 @@ -39,7 +39,7 @@
  .Os BSD 4.2
  .Sh NAME
  .Nm fsplit
 -.Nd split a multi-routine Fortran file into individual files
 +.Nd split a multi-routine Fortran 77 file into individual files
  .Sh SYNOPSIS
  .Nm fsplit
  .Op Fl e Ar efile
 @@ -47,8 +47,8 @@
  .Op Ar file
  .Sh DESCRIPTION
  .Nm Fsplit
 -takes as input either a file or standard input containing Fortran source code.
 -It attempts to split the input into separate routine files of the
 +takes as input either a file or standard input containing Fortran 77 source
 +code.  It attempts to split the input into separate routine files of the
  form
  .Ar name.f ,
  where
 @@ -104,3 +104,7 @@
  .Fl e
  for unnamed main programs and block data subprograms since you must
  predict the created file name.
 +.Pp
 +.Nm
 +can be used with Fortran 77 and older source code.  It understands neither
 +Fortran 90/95 syntax nor free form source files.
 diff -r -u /usr/src/usr.bin/fsplit/fsplit.c fsplit/fsplit.c
 --- /usr/src/usr.bin/fsplit/fsplit.c	Tue Sep  7 11:59:34 1999
 +++ fsplit/fsplit.c	Tue Dec 14 10:59:13 1999
 @@ -51,6 +51,7 @@
  #include <ctype.h>
  #include <err.h>
  #include <stdio.h>
 +#include <stdlib.h>
  #include <string.h>
  #include <sys/types.h>
  #include <sys/stat.h>
 @@ -90,11 +91,8 @@
  
  #define TRUE 1
  #define FALSE 0
 -int	extr = FALSE,
 -	extrknt = -1,
 -	extrfnd[100];
 -char	extrbuf[1000],
 -	*extrnames[100];
 +int extr = FALSE, extrknt = -1, *extrfnd;
 +char **extrnames;
  struct stat sbuf;
  
  #define trim(p)	while (*p == ' ' || *p == '\t') p++
 @@ -103,58 +101,74 @@
  void  get_name __P((char *, int));
  char *functs __P((char *));
  int   lend __P((void));
 -int   lname __P((char *));
 +int   lname __P((char *, int));
  char *look __P((char *, char *));
  int   saveit __P((char *));
 -int   scan_name __P((char *, char *));
 +int   scan_name __P((char *, char *, int));
  char *skiplab __P((char *));
  static void usage __P((void));
  
  int
  main(argc, argv)
 +int argc;
  char **argv;
  {
 +	extern int optind;
 +	extern char *optarg;
 +
  	register FILE *ofp;	/* output file */
  	register int rv;	/* 1 if got card in output file, 0 otherwise */
  	register char *ptr;
  	int nflag,		/* 1 if got name of subprog., 0 otherwise */
  		retval,
  		i;
 -	char name[20],
 -		*extrptr = extrbuf;
 +	char name[20];
  
 -	/*  scan -e options */
 -	while ( argc > 1  && argv[1][0] == '-' && argv[1][1] == 'e') {
 +	if (argc > 2) {
  		extr = TRUE;
 -		ptr = argv[1] + 2;
 -		if(!*ptr) {
 -			argc--;
 -			argv++;
 -			if(argc <= 1)
 +
 +		extrfnd = (int *) malloc(argc * sizeof(int));
 +		if (extrfnd == NULL)
 +			errx(1, NULL);
 +
 +		extrnames = (char **) malloc(argc * sizeof(char *));
 +		if (extrnames == NULL)
 +			errx(1, NULL);
 +
 +		while ((i = getopt(argc, argv, "e:")) != -1) {
 +			switch (i) {
 +			case 'e':
 +				extrknt++;
 +				extrfnd[extrknt] = FALSE;
 +				extrnames[extrknt] = optarg;
 +				break;
 +			default:
  				usage();
 -			ptr = argv[1];
 +			}
  		}
 -		extrknt = extrknt + 1;
 -		extrnames[extrknt] = extrptr;
 -		extrfnd[extrknt] = FALSE;
 -		while(*ptr) *extrptr++ = *ptr++;
 -		*extrptr++ = 0;
 +
 +		argc -= optind;
 +		argv += optind;
 +	} else {
  		argc--;
  		argv++;
  	}
  
 -	if (argc > 2)
 +	if (argc > 1)
  		usage();
 -	else if (argc == 2) {
 -		if ((ifp = fopen(argv[1], "r")) == NULL)
 -			errx(1, "cannot open %s", argv[1]);
 +	else if (argc == 1) {
 +		if ((ifp = fopen(*argv, "r")) == NULL)
 +			errx(1, "cannot open %s", *argv);
  	}
  	else
  		ifp = stdin;
 +
      for(;;) {
  	/* look for a temp file that doesn't correspond to an existing file */
  	get_name(x, 3);
  	ofp = fopen(x, "w");
 +	if (ofp == NULL) 
 +		errx(1, "can not open %s", x);
  	nflag = 0;
  	rv = 0;
  	while (getline() > 0) {
 @@ -163,7 +177,7 @@
  		if (lend())		/* look for an 'end' statement */
  			break;
  		if (nflag == 0)		/* if no name yet, try and find one */
 -			nflag = lname(name);
 +			nflag = lname(name, 20);
  	}
  	fclose(ofp);
  	if (rv == 0) {			/* no lines in file, forget the file */
 @@ -198,6 +212,8 @@
  	else
  		unlink(x);
      }
 +
 +	return 0;
  }
  
  static void
 @@ -293,8 +309,9 @@
  		name and put in arg string. invent name for unnamed
  		block datas and main programs.		*/
  int
 -lname(s)
 +lname(s, len)
  char *s;
 +int len;
  {
  #	define LINESIZE 80
  	register char *ptr, *p;
 @@ -324,18 +341,18 @@
  	if ((ptr = look(line, "subroutine")) != 0 ||
  	    (ptr = look(line, "function")) != 0 ||
  	    (ptr = functs(line)) != 0) {
 -		if(scan_name(s, ptr)) return(1);
 +		if(scan_name(s, ptr, len)) return(1);
  		strcpy( s, x);
  	} else if((ptr = look(line, "program")) != 0) {
 -		if(scan_name(s, ptr)) return(1);
 +		if(scan_name(s, ptr, len)) return(1);
  		get_name( mainp, 4);
  		strcpy( s, mainp);
  	} else if((ptr = look(line, "blockdata")) != 0) {
 -		if(scan_name(s, ptr)) return(1);
 +		if(scan_name(s, ptr, len)) return(1);
  		get_name( blkp, 6);
  		strcpy( s, blkp);
  	} else if((ptr = functs(line)) != 0) {
 -		if(scan_name(s, ptr)) return(1);
 +		if(scan_name(s, ptr, len)) return(1);
  		strcpy( s, x);
  	} else {
  		get_name( mainp, 4);
 @@ -345,17 +362,22 @@
  }
  
  int
 -scan_name(s, ptr)
 +scan_name(s, ptr, len)
  char *s, *ptr;
 +int len;
  {
 +	int cnt = 0;
  	char *sptr;
  
  	/* scan off the name */
  	trim(ptr);
  	sptr = s;
  	while (*ptr != '(' && *ptr != '\n') {
 -		if (*ptr != ' ' && *ptr != '\t')
 +		if (*ptr != ' ' && *ptr != '\t') {
  			*sptr++ = *ptr;
 +            cnt++;
 +            if (cnt == len - 3) break;
 +        }
  		ptr++;
  	}
  
 

From: Sheldon Hearn <sheldonh@uunet.co.za>
To: "Steven G. Kargl" <kargl@troutmask.apl.washington.edu>
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: bin/15471: fsplit buffer overflow fixes 
Date: Tue, 21 Dec 1999 13:42:51 +0200

 On Tue, 14 Dec 1999 11:06:41 PST, "Steven G. Kargl" wrote:
 
 > +	extern int optind;
 > +	extern char *optarg;
 > +
 
 I think these are declared in unistd.h, which you should always include
 for getopt(3).
 
 > +		extrfnd = (int *) malloc(argc * sizeof(int));
 > +		if (extrfnd == NULL)
 > +			errx(1, NULL);
 
 The style(9) way of doing this seems to be to put the malloc, assignment
 and evaluation in the if condition.  The same applies to your fopen()
 later.
 
 If I were to commit with these minor deviations from your patch, would
 you be happy?
 
 Ciao,
 Sheldon.
 
Responsible-Changed-From-To: freebsd-bugs->kris 
Responsible-Changed-By: kris 
Responsible-Changed-When: Sun Jan 30 13:15:59 PST 2000 
Responsible-Changed-Why:  
I'll either apply these fixes to stable or rip it out like in -current.. 
haven't decided yet :) 
State-Changed-From-To: open->closed 
State-Changed-By: dwmalone 
State-Changed-When: Tue Feb 6 09:50:16 PST 2001 
State-Changed-Why:  
Closed at submitters request. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=15471 
>Unformatted:
