[net.unix-wizards] bugs in -ltermlib termcap.c

hansen@pegasus.UUCP (Tony L. Hansen) (04/27/85)

I've found a few bugs in the termcap library that still exist in
4.2BSD.

1: tgetent() cannot be called more than once in a program (hopcount doesn't
   get rezeroed).
2: an entry with ":tc=foo" but no trailing colon will cause coredumps.
3. buffer overflows can still occur, causing coredumps.
4. in tc=foo, foo can only be 16 chars long. this is not checked.

Apply patch at will.

					Tony Hansen
					pegasus!hansen


*** /tmp/termcap.c	Fri Apr 26 16:41:17 1985
--- /tmp/ntermcap.c	Fri Apr 26 16:57:40 1985
***************
*** 2,8
  static char sccsid[] = "@(#)termcap.c	4.1 (Berkeley) 6/27/83";
  #endif
  
! #define	BUFSIZ		1024
  #define MAXHOP		32	/* max number of tc= indirections */
  #define	E_TERMCAP	"/etc/termcap"
  

--- 2,8 -----
  static char sccsid[] = "@(#)termcap.c	4.1 (Berkeley) 6/27/83";
  #endif
  
! #define	TBUFSIZE	2048
  #define MAXHOP		32	/* max number of tc= indirections */
  #define	E_TERMCAP	"/etc/termcap"
  
***************
*** 36,41
  tgetent(bp, name)
  	char *bp, *name;
  {
  	register char *cp;
  	register int c;
  	register int i = 0, cnt = 0;

--- 36,59 -----
  tgetent(bp, name)
  	char *bp, *name;
  {
+ 	/* Tony Hansen */
+ 	int ret;
+ 	hopcount = 0;
+ 	ret = _tgetent(bp, name);
+ 	/*
+ 	  There is some sort of bug in the check way down below to prevent
+ 	  buffer overflow. I really don't want to track it down, so I
+ 	  upped the standard buffer size and check here to see if the created
+ 	  buffer is larger than the old buffer size.
+ 	*/
+ 	if (strlen(bp) >= 1024)
+ 		write(2,"Termcap entry too long\n", 23);
+ 	return ret;
+ }
+ 
+ static _tgetent(bp, name)
+ 	char *bp, *name;
+ {
  	register char *cp;
  	register int c;
  	register int i = 0, cnt = 0;
***************
*** 39,45
  	register char *cp;
  	register int c;
  	register int i = 0, cnt = 0;
! 	char ibuf[BUFSIZ];
  	char *cp2;
  	int tf;
  

--- 57,63 -----
  	register char *cp;
  	register int c;
  	register int i = 0, cnt = 0;
! 	char ibuf[TBUFSIZ];
  	char *cp2;
  	int tf;
  
***************
*** 77,83
  		cp = bp;
  		for (;;) {
  			if (i == cnt) {
! 				cnt = read(tf, ibuf, BUFSIZ);
  				if (cnt <= 0) {
  					close(tf);
  					return (0);

--- 95,101 -----
  		cp = bp;
  		for (;;) {
  			if (i == cnt) {
! 				cnt = read(tf, ibuf, TBUFSIZE);
  				if (cnt <= 0) {
  					close(tf);
  					return (0);
***************
*** 92,98
  				}
  				break;
  			}
! 			if (cp >= bp+BUFSIZ) {
  				write(2,"Termcap entry too long\n", 23);
  				break;
  			} else

--- 110,116 -----
  				}
  				break;
  			}
! 			if (cp >= bp+TBUFSIZE) {
  				write(2,"Termcap entry too long\n", 23);
  				break;
  			} else
***************
*** 120,125
  tnchktc()
  {
  	register char *p, *q;
  	char tcname[16];	/* name of similar terminal */
  	char tcbuf[BUFSIZ];
  	char *holdtbuf = tbuf;

--- 138,145 -----
  tnchktc()
  {
  	register char *p, *q;
+ #define TERMNAMESIZE 16
+ 	char tcname[TERMNAMESIZE];	/* name of similar terminal */
  	char tcname[16];	/* name of similar terminal */
  	char tcbuf[TBUFSIZE];
  	char *holdtbuf = tbuf;
***************
*** 121,127
  {
  	register char *p, *q;
  	char tcname[16];	/* name of similar terminal */
! 	char tcbuf[BUFSIZ];
  	char *holdtbuf = tbuf;
  	int l;
  

--- 141,147 -----
  #define TERMNAMESIZE 16
  	char tcname[TERMNAMESIZE];	/* name of similar terminal */
  	char tcname[16];	/* name of similar terminal */
! 	char tcbuf[TBUFSIZE];
  	char *holdtbuf = tbuf;
  	int l;
  
***************
*** 135,140
  	/* p now points to beginning of last field */
  	if (p[0] != 't' || p[1] != 'c')
  		return(1);
  	strcpy(tcname,p+3);
  	q = tcname;
  	while (q && *q != ':')

--- 155,161 -----
  	/* p now points to beginning of last field */
  	if (p[0] != 't' || p[1] != 'c')
  		return(1);
+ 	strncpy(tcname,p+3, TERMNAMESIZE);	/* TLH */
  	strcpy(tcname,p+3);
  	q = tcname;
  	while (*q && *q != ':')
***************
*** 137,143
  		return(1);
  	strcpy(tcname,p+3);
  	q = tcname;
! 	while (q && *q != ':')
  		q++;
  	*q = 0;
  	if (++hopcount > MAXHOP) {

--- 158,164 -----
  	strncpy(tcname,p+3, TERMNAMESIZE);	/* TLH */
  	strcpy(tcname,p+3);
  	q = tcname;
! 	while (*q && *q != ':')
  		q++;
  	*q = 0;
  	if (++hopcount > MAXHOP) {
***************
*** 144,150
  		write(2, "Infinite tc= loop\n", 18);
  		return (0);
  	}
! 	if (tgetent(tcbuf, tcname) != 1)
  		return(0);
  	for (q=tcbuf; *q != ':'; q++)
  		;

--- 165,171 -----
  		write(2, "Infinite tc= loop\n", 18);
  		return (0);
  	}
! 	if (_tgetent(tcbuf, tcname) != 1)
  		return(0);
  	for (q=tcbuf; *q != ':'; q++)
  		;
***************
*** 149,155
  	for (q=tcbuf; *q != ':'; q++)
  		;
  	l = p - holdtbuf + strlen(q);
! 	if (l > BUFSIZ) {
  		write(2, "Termcap entry too long\n", 23);
  		q[BUFSIZ - (p-tbuf)] = 0;
  	}

--- 170,176 -----
  	for (q=tcbuf; *q != ':'; q++)
  		;
  	l = p - holdtbuf + strlen(q);
! 	if (l > TBUFSIZE) {
  		write(2, "Termcap entry too long\n", 23);
  		q[TBUFSIZE - (p-tbuf)] = 0;
  	}
***************
*** 151,157
  	l = p - holdtbuf + strlen(q);
  	if (l > BUFSIZ) {
  		write(2, "Termcap entry too long\n", 23);
! 		q[BUFSIZ - (p-tbuf)] = 0;
  	}
  	strcpy(p, q+1);
  	tbuf = holdtbuf;

--- 172,178 -----
  	l = p - holdtbuf + strlen(q);
  	if (l > TBUFSIZE) {
  		write(2, "Termcap entry too long\n", 23);
! 		q[TBUFSIZE - (p-tbuf)] = 0;
  	}
  	strcpy(p, q+1);
  	tbuf = holdtbuf;

hakanson@orstcs.UUCP (hakanson) (05/14/85)

The following is in reference to the termcap (termlib) fixes posted
by pegasus!hansen.  Our mailer couldn't find a path to pegasus, so here
is what I would've sent directly if I could have:

I finally got around to examining the patches to termcap(3), and noticed
a few problems with them.  The contextual diff seemed to show a few lines
that had been inserted before lines they had been meant to replace (easily
fixed, of course).  But the patch to avoid core dumps (doubling the size
of the buffer) only puts off the core dump until an entry gets too big for
the larger buffer.  Since I felt this was an inelegant fix, I bit the bullet
and found the bug(s) that caused the problem, and patched that part myself.

I'd appreciate hearing about anything I've broken below.  BTW, these
patches work fine on our VAX 750 running 4.2bsd, etc.....


/* rcsdiff -c -r1.1 -r1.2 termcap.c */
RCS file: RCS/termcap.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -c -r1.1 -r1.2
*** /tmp/,RCSt1025137   Tue May  7 12:28:53 1985
--- /tmp/,RCSt2025137   Tue May  7 12:28:57 1985
***************
*** 1,5
  #ifndef lint
  static char sccsid[] = "@(#)termcap.c 4.1 (Berkeley) 6/27/83";
  #endif

  #define       BUFSIZ          1024

--- 1,6 -----
  #ifndef lint
  static char sccsid[] = "@(#)termcap.c 4.1 (Berkeley) 6/27/83";
+ static char rcsid[] = "$Header: termcap.c,v 1.2 85/05/06 10:31:50 hakanson Exp $";
  #endif

  #define       BUFSIZ          1024
***************
*** 20,25
   * in string capabilities.  We don't use stdio because the editor
   * doesn't, and because living w/o it is not hard.
   */

  static        char *tbuf;
  static        int hopcount;   /* detect infinite loops in termcap, init 0 */

--- 21,38 -----
   * in string capabilities.  We don't use stdio because the editor
   * doesn't, and because living w/o it is not hard.
   */
+ /*
+  * Modified 05/06/85 by R. Marion Hakanson (hakanson@oregon-state).
+  *  Fixed the following bugs (from Tony Hansen (pegasus!hansen)):
+  *
+  * 1: tgetent() cannot be called more than once in a program (hopcount
+  *    doesn't get rezeroed).
+  * 2: an entry with ":tc=foo" but no trailing colon will cause coredumps.
+  * 3. buffer overflows can still occur, causing coredumps.
+  * 4. in tc=foo, foo can only be 16 chars long. this is not checked.
+  *
+  * The patches for #1, #2 & #4 came from hansen;  I fixed #3.
+  */

  static        char *tbuf;
  static        int hopcount;   /* detect infinite loops in termcap, init 0 */
***************
*** 36,41
  tgetent(bp, name)
        char *bp, *name;
  {
        register char *cp;
        register int c;
        register int i = 0, cnt = 0;

--- 49,61 -----
  tgetent(bp, name)
        char *bp, *name;
  {
+       hopcount = 0;
+       return (_tgetent(bp, name));
+ }
+
+ static _tgetent(bp, name)
+       char *bp, *name;
+ {
        register char *cp;
        register int c;
        register int i = 0, cnt = 0;
***************
*** 93,98
                                break;
                        }
                        if (cp >= bp+BUFSIZ) {
                                write(2,"Termcap entry too long\n", 23);
                                break;
                        } else

--- 113,119 -----
                                break;
                        }
                        if (cp >= bp+BUFSIZ) {
+                               cp = bp+BUFSIZ-1;
                                write(2,"Termcap entry too long\n", 23);
                                break;
                        } else
***************
*** 117,122
   * entries to say "like an HP2621 but doesn't turn on the labels".
   * Note that this works because of the left to right scan.
   */
  tnchktc()
  {
        register char *p, *q;

--- 138,145 -----
   * entries to say "like an HP2621 but doesn't turn on the labels".
   * Note that this works because of the left to right scan.
   */
+ #define NAMESIZE 16
+
  tnchktc()
  {
        register char *p, *q;
***************
*** 120,126
  tnchktc()
  {
        register char *p, *q;
!       char tcname[16];        /* name of similar terminal */
        char tcbuf[BUFSIZ];
        char *holdtbuf = tbuf;
        int l;

--- 143,149 -----
  tnchktc()
  {
        register char *p, *q;
!       char tcname[NAMESIZE];  /* name of similar terminal */
        char tcbuf[BUFSIZ];
        char *holdtbuf = tbuf;
        int l;
***************
*** 135,141
        /* p now points to beginning of last field */
        if (p[0] != 't' || p[1] != 'c')
                return(1);
!       strcpy(tcname,p+3);
        q = tcname;
        while (q && *q != ':')
                q++;

--- 158,165 -----
        /* p now points to beginning of last field */
        if (p[0] != 't' || p[1] != 'c')
                return(1);
!       strncpy(tcname,p+3,NAMESIZE);
!       tcname[NAMESIZE-1] = 0;
        q = tcname;
        while (*q && *q != ':')
                q++;
***************
*** 137,143
                return(1);
        strcpy(tcname,p+3);
        q = tcname;
!       while (q && *q != ':')
                q++;
        *q = 0;
        if (++hopcount > MAXHOP) {

--- 161,167 -----
        strncpy(tcname,p+3,NAMESIZE);
        tcname[NAMESIZE-1] = 0;
        q = tcname;
!       while (*q && *q != ':')
                q++;
        *q = 0;
        if (++hopcount > MAXHOP) {
***************
*** 144,150
                write(2, "Infinite tc= loop\n", 18);
                return (0);
        }
!       if (tgetent(tcbuf, tcname) != 1)
                return(0);
        for (q=tcbuf; *q != ':'; q++)
                ;

--- 168,174 -----
                write(2, "Infinite tc= loop\n", 18);
                return (0);
        }
!       if (_tgetent(tcbuf, tcname) != 1)
                return(0);
        for (q=tcbuf; *q != ':'; q++)
                ;
***************
*** 151,157
        l = p - holdtbuf + strlen(q);
        if (l > BUFSIZ) {
                write(2, "Termcap entry too long\n", 23);
!               q[BUFSIZ - (p-tbuf)] = 0;
        }
        strcpy(p, q+1);
        tbuf = holdtbuf;

--- 175,181 -----
        l = p - holdtbuf + strlen(q);
        if (l > BUFSIZ) {
                write(2, "Termcap entry too long\n", 23);
!               q[BUFSIZ - (p-holdtbuf)] = 0;
        }
        strcpy(p, q+1);
        tbuf = holdtbuf;
/* end diff listing */

Marion Hakanson         CSnet:  hakanson%oregon-state@csnet-relay
                        UUCP :  {hp-pcd,tekchips}!orstcs!hakanson