Bug 15471

Summary: Fix several buffer overflows
Product: Base System Reporter: Steven G. Kargl <kargl>
Component: binAssignee: Kris Kennaway <kris>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.0-CURRENT   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description Steven G. Kargl 1999-12-13 21:00:02 UTC
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).

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.

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
Comment 1 Sheldon Hearn 1999-12-14 12:54:59 UTC
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.
Comment 2 Steven G. Kargl 1999-12-14 19:06:41 UTC
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++;
 	}
Comment 3 Sheldon Hearn 1999-12-21 11:42:51 UTC
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.
Comment 4 Kris Kennaway freebsd_committer freebsd_triage 2000-01-30 21:15:59 UTC
Responsible Changed
From-To: freebsd-bugs->kris

I'll either apply these fixes to stable or rip it out like in -current.. 
haven't decided yet :) 
Comment 5 dwmalone freebsd_committer freebsd_triage 2001-02-06 17:50:16 UTC
State Changed
From-To: open->closed

Closed at submitters request.