Subject: lib/17352: libcurses core dumps if screen wider than 1024 columns
To: None <gnats-bugs@gnats.netbsd.org>
From: None <dsl@l8s.co.uk>
List: netbsd-bugs
Date: 06/21/2002 10:52:10
>Number:         17352
>Category:       lib
>Synopsis:       libcurses core dumps if screen wider than 1024 columns
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Jun 21 02:53:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     David Laight
>Release:        NetBSD 1.5ZC (also current 21/6/02)
>Organization:
	no
>Environment:
System: NetBSD snowdrop 1.5ZC NetBSD 1.5ZC (GENERIC) #10: Thu May 16 12:50:04 BST 2002 dsl@snowdrop:/oldroot/usr/bsd-current/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:
	There are a few buffers on size 1024 in libcurses that cause
	grief on very wide terminals.
	Additionaly the cursor positioning routing (in libterm)
	won't generate lines/columns > 999.
	I've also fixed bugs that stop libcurses compiling if DEBUG
	is defined.
>How-To-Repeat:
	Run a 'vi' that has had the 500 column limit removed on a
	very wide terminal (eg xterm with font 'unreadable').
>Fix:
	Apply the following patches:

	(The fix in scanw.c stops the buffer overwrite.
	OTOH I'm not at all sure what this code is playing at!)


Index: color.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/color.c,v
retrieving revision 1.17
diff -u -r1.17 color.c
--- color.c	2002/01/02 10:38:27	1.17
+++ color.c	2002/06/21 09:34:26
@@ -318,8 +318,8 @@
 	pair = PAIR_NUMBER((u_int32_t)attr);
 #ifdef DEBUG
 	__CTRACE("__set_color: %d, %d, %d\n", pair,
-		 _cursesi_screen->pairs[pair].fore,
-		 _cursesi_screen->pairs[pair].back);
+		 _cursesi_screen->colour_pairs[pair].fore,
+		 _cursesi_screen->colour_pairs[pair].back);
 #endif
 	switch (_cursesi_screen->color_type) {
 	/* Set ANSI forground and background colours */
Index: cr_put.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/cr_put.c,v
retrieving revision 1.20
diff -u -r1.20 cr_put.c
--- cr_put.c	2000/12/19 21:34:24	1.20
+++ cr_put.c	2002/06/21 09:34:38
@@ -97,7 +97,7 @@
 	int     in_refresh;
 {
 	int     c, l;
-	char   cgp[1024];
+	char	cgp[64];		/* 64 bytes is plenty for terminfo 'move cursor' */
 
 	if (destcol >= COLS) {
 		destline += destcol / COLS;
@@ -166,9 +166,7 @@
 	}
 	if (destline < outline && !(__CA || __tc_up))
 		destline = outline;
-	if (__CA) {
-		t_goto(NULL, __tc_cm, destcol, destline, cgp, 1023);
-
+	if (__CA && !t_goto(NULL, __tc_cm, destcol, destline, cgp, sizeof cgp - 1)) {
 		/*
 		 * Need this condition due to inconsistent behavior
 		 * of backspace on the last column.
Index: ctrace.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/ctrace.c,v
retrieving revision 1.12
diff -u -r1.12 ctrace.c
--- ctrace.c	2002/05/26 17:01:38	1.12
+++ ctrace.c	2002/06/21 09:34:48
@@ -45,6 +45,7 @@
 #ifdef DEBUG
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 
 #include <sys/time.h>
 #include <string.h>
@@ -65,10 +66,18 @@
         static int seencr = 1;
 	va_list ap;
 
-	if (tracefp == NULL)
-		tracefp = fopen(TFILE, "w");
-	if (tracefp == NULL)
+	if (tracefp == (void *)~0)
+	    return;
+
+	if (tracefp == NULL) {
+		char *tf = getenv( "CURSES_TRACE_FILE" );
+		if (!tf || strcmp( tf, "<none>"))
+		    tracefp = fopen( tf ? tf : TFILE, "w");
+	}
+	if (tracefp == NULL) {
+		tracefp = (void *)~0;
 		return;
+	}
 	gettimeofday(&tv, NULL);
         if (seencr) {
                 gettimeofday(&tv, NULL);
Index: cur_hash.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/cur_hash.c,v
retrieving revision 1.9
diff -u -r1.9 cur_hash.c
--- cur_hash.c	2000/04/15 13:17:03	1.9
+++ cur_hash.c	2002/06/21 09:35:02
@@ -50,12 +50,14 @@
 /*
  * __hash() is "hashpjw" from the Dragon Book, Aho, Sethi & Ullman, p.436.
  */
+
 u_int
-__hash(char *s, int len)
+__hash_more(const void  *v_s, size_t len, u_int h)
 {
-	u_int   h, g, i;
+	u_int   g;
+	size_t	i;
+	const char *s = v_s;
 
-	h = 0;
 	i = 0;
 	while (i < len) {
 		h = (h << 4) + s[i];
Index: curses_private.h
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/curses_private.h,v
retrieving revision 1.20
diff -u -r1.20 curses_private.h
--- curses_private.h	2002/01/02 10:38:27	1.20
+++ curses_private.h	2002/06/21 09:35:25
@@ -231,7 +231,7 @@
 	unsigned int len;
 	int meta_state;
 	char pad_char;
-	char ttytype[1024];
+	char ttytype[128];	/* terminal name - filled by __longname */
 	int endwin;
 };
 
@@ -255,7 +255,8 @@
 int      _cursesi_setterm(char *type, SCREEN *screen);
 int      _cursesi_wnoutrefresh(SCREEN *screen, WINDOW *win);
 int	 __delay(void);
-unsigned int __hash(char *s, int len);
+unsigned int __hash_more(const void *s, size_t len, unsigned int hash_in);
+#define __hash(s,len) __hash_more(s,len,0u)
 void	 __id_subwins(WINDOW *orig);
 void	 __init_getch(SCREEN *screen);
 void	 __init_acs(SCREEN *screen);
Index: refresh.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/refresh.c,v
retrieving revision 1.45
diff -u -r1.45 refresh.c
--- refresh.c	2002/01/02 10:38:28	1.45
+++ refresh.c	2002/06/21 09:35:39
@@ -240,8 +240,7 @@
 		for (wy = 0; wy < win->maxy; wy++) {
 			wlp = win->lines[wy];
 			if (wlp->flags & __ISDIRTY)
-				wlp->hash = __hash((char *)(void *)wlp->line,
-				    (int) (win->maxx * __LDATASIZE));
+				wlp->hash = __hash(wlp->line, win->maxx * __LDATASIZE);
 		}
 
 	if ((win->flags & __CLEAROK) || (curscr->flags & __CLEAROK) ||
@@ -721,9 +720,11 @@
 	__LINE *clp, *tmp1, *tmp2;
 	int	bsize, curs, curw, starts, startw, i, j;
 	int	n, target, cur_period, bot, top, sc_region;
-	__LDATA buf[1024];
+	static __LDATA buf[128];		/* only ever spaces */
 	u_int	blank_hash;
 	attr_t	bcolor;
+	static	u_int last_hash;
+	static	size_t last_hash_len;
 
 #ifdef __GNUC__
 	curs = curw = starts = startw = 0;	/* XXX gcc -Wuninitialized */
@@ -865,15 +866,26 @@
 	}
 #endif
 
+	/* Initialise buf to a block of space chars... */
+	if (!buf[0].ch)
+		for (i = 0; i < 128; i++) {
+			buf[i].ch = ' ';
+			buf[i].bch = ' ';
+			buf[i].attr = 0;
+			buf[i].battr = 0;
+		}
+
 	/* So we don't have to call __hash() each time */
-	for (i = 0; i < __virtscr->maxx; i++) {
-		buf[i].ch = ' ';
-		buf[i].bch = ' ';
-		buf[i].attr = 0;
-		buf[i].battr = 0;
-	}
-	blank_hash = __hash((char *)(void *)buf,
-	    (int) (__virtscr->maxx * __LDATASIZE));
+	if (__virtscr->maxx != last_hash_len) {
+		blank_hash = 0;
+		for (i = __virtscr->maxx; i > 128; i-= 128 )
+			blank_hash = __hash_more( buf, sizeof buf, blank_hash );
+		blank_hash = __hash_more( buf, i * sizeof buf[0], blank_hash );
+		/* cache result in static data - screen width doesn't change often! */
+		last_hash_len = __virtscr->maxx;
+		last_hash = blank_hash;
+	} else
+		blank_hash = last_hash;
 
 	/*
 	 * Perform the rotation to maintain the consistency of curscr.
@@ -930,10 +942,13 @@
 		} else
 			if ((n > 0 && target >= top && target < top + n) ||
 			    (n < 0 && target <= bot && target > bot + n)) {
-				if (clp->hash != blank_hash || memcmp(clp->line,
-				    buf, (size_t) __virtscr->maxx * __LDATASIZE) !=0) {
-					(void)memcpy(clp->line,  buf,
-					    (size_t) __virtscr->maxx * __LDATASIZE);
+				if (clp->hash != blank_hash
+				    || memcmp(clp->line, clp->line + 1,
+						    (__virtscr->maxx - 1) * __LDATASIZE)
+				    || memcmp( clp->line, buf, __LDATASIZE) ) {
+					for (i = __virtscr->maxx; i > 128; i-= 128 )
+						memcpy( clp->line + i - 128, buf, sizeof buf );
+					memcpy( clp->line , buf, i * sizeof buf[0] );
 #ifdef DEBUG
 					__CTRACE("-- blanked out: dirty\n");
 #endif
Index: scanw.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/scanw.c,v
retrieving revision 1.16
diff -u -r1.16 scanw.c
--- scanw.c	2002/05/26 17:01:38	1.16
+++ scanw.c	2002/06/21 09:35:49
@@ -122,6 +122,6 @@
 
 	char    buf[1024];
 
-	return (wgetstr(win, buf) == OK ?
+	return (wgetnstr(win, buf, sizeof buf) == OK ?
 	    vsscanf(buf, fmt, ap) : ERR);
 }
Index: setterm.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/setterm.c,v
retrieving revision 1.31
diff -u -r1.31 setterm.c
--- setterm.c	2002/01/02 10:38:29	1.31
+++ setterm.c	2002/06/21 09:36:02
@@ -123,9 +123,7 @@
 int
 _cursesi_setterm(char *type, SCREEN *screen)
 {
-	static char cm_buff[1024], tc[1024], *tcptr;
 	int unknown;
-	size_t limit;
 	struct winsize win;
 	char *p;
 
@@ -187,11 +185,14 @@
 	 * Test for cursor motion capability.
 	 *
 	 */
-	if (t_goto(NULL, screen->tc_cm, 0, 0, cm_buff, 1023) < 0) {
-		screen->CA = 0;
-		screen->tc_cm = 0;
-	} else
-		screen->CA = 1;
+	{
+		char cm_buff[64];
+		if (t_goto(NULL, screen->tc_cm, 0, 0, cm_buff, sizeof cm_buff - 1) < 0) {
+			screen->CA = 0;
+			screen->tc_cm = 0;
+		} else
+			screen->CA = 1;
+	}
 
         /*
 	 * set the pad char, only take the first char of the pc capability
@@ -199,14 +200,19 @@
 	 */
 	screen->pad_char = screen->tc_pc ? screen->tc_pc[0] : 0; 
 
+	/* Get full name of terminal */
 	if (unknown) {
 		strcpy(screen->ttytype, "dumb");
 	} else {
-		tcptr = tc;
-		limit = 1023;
-		if (t_getterm(screen->cursesi_genbuf, &tcptr, &limit) < 0)
+		char *tcptr;
+		size_t limit = 0;
+		if (t_getterm(screen->cursesi_genbuf, 0, &limit))
+			return ERR;
+		tcptr = malloc( limit + 1 );
+		if (!tcptr || t_getterm(screen->cursesi_genbuf, &tcptr, 0) < 0)
 			return ERR;
-		__longname(tc, screen->ttytype);
+		__longname(tcptr, screen->ttytype);
+		free( tcptr );
 	}
 
 	/* If no scrolling commands, no quick change. */
@@ -351,7 +357,7 @@
 		*(tmp + 1) = *(namp + 1);
 		*vp++ = t_getnum(screen->cursesi_genbuf, tmp);
 #ifdef DEBUG
-		__CTRACE("%2.2s = %d\n", namp, *vp[-1]);
+		__CTRACE("%2.2s = %d\n", namp, vp[-1]);
 #endif
 		namp += 2;
 		screen->int_count++;
@@ -367,7 +373,7 @@
 		*(tmp + 1) = *(namp + 1);
 		*sp++ = t_agetstr(screen->cursesi_genbuf, tmp);
 #ifdef DEBUG
-		__CTRACE("%2.2s = %s", namp, *sp[-1] == NULL ? "NULL\n" : "\"");
+		__CTRACE("%2.2s = %s", namp, sp[-1] == NULL ? "NULL\n" : "\"");
 		if (sp[-1] != NULL) {
 			for (cp = sp[-1]; *cp; cp++)
 				__CTRACE("%s", unctrl(*cp));
Index: tscroll.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libcurses/tscroll.c,v
retrieving revision 1.10
diff -u -r1.10 tscroll.c
--- tscroll.c	2002/05/26 17:01:38	1.10
+++ tscroll.c	2002/06/21 09:36:16
@@ -44,6 +44,7 @@
 #endif
 #endif				/* not lint */
 
+#include <string.h>
 #include "curses.h"
 #include "curses_private.h"
 
Index: ../libterm/tgoto.c
===================================================================
RCS file: /cvsroot/basesrc/lib/libterm/tgoto.c,v
retrieving revision 1.19
diff -u -r1.19 tgoto.c
--- tgoto.c	2001/01/09 07:18:50	1.19
+++ tgoto.c	2002/06/21 09:36:44
@@ -114,6 +114,9 @@
 	int c;
 	int oncol = 0;
 	int which = destline;
+	char *buf_lim = buffer + limit;
+	char dig_buf[ 3 * sizeof which ];
+	int k;
 
 	/* CM is checked below */
 	_DIAGASSERT(buffer != NULL);
@@ -128,70 +131,72 @@
 
 	if (cp == 0) {
 		errno = EINVAL;
-toohard:
 		return -1;
 	}
 	added[0] = '\0';
 	while ((c = *cp++) != '\0') {
-		if (c != '%') {
-copy:
+		if (c != '%' || (c = *cp++) == '%') {
 			*dp++ = c;
-			if (dp >= &buffer[limit])
-			{
+			if (dp >= buf_lim) {
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
 			continue;
 		}
-		switch (c = *cp++) {
+
+		switch (c) {
 
 #ifdef CM_N
 		case 'n':
 			destcol ^= 0140;
 			destline ^= 0140;
-			goto setwhich;
+			/* flip oncol here so it doesn't actually change */
+			oncol = 1 - oncol;
+			break;
 #endif
 
 		case 'd':
-			if (which < 10)
-				goto one;
-			if (which < 100)
-				goto two;
-			/* FALLTHROUGH */
+			/* Generate digits into temp buffer in reverse order */
+			k = 0;
+			do
+				dig_buf[ k++ ] = which % 10 | '0';
+			while ((which /= 10));
+			if (dp + k >= buf_lim) {
+				errno = E2BIG;
+				return -1;
+			}
+			/* then unwind into callers buffer */
+			do
+				*dp++ = dig_buf[ --k ];
+			while (k);
+			break;
 
 		case '3':
 			if (which >= 1000) {
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
 			*dp++ = (which / 100) | '0';
-			if (dp >= &buffer[limit]) {
+			if (dp >= buf_lim) {
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
 			which %= 100;
 			/* FALLTHROUGH */
 
 		case '2':
-two:
 			*dp++ = which / 10 | '0';
-			if (dp >= &buffer[limit]) {
+			if (dp >= buf_lim) {
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
-one:
 			*dp++ = which % 10 | '0';
-			if (dp >= &buffer[limit]) {
+			if (dp >= buf_lim) {
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
+			break;
 			
-swap:
-			oncol = 1 - oncol;
-setwhich:
-			which = oncol ? destcol : destline;
-			continue;
-
 #ifdef CM_GT
 		case '>':
 			if (which > *cp++)
@@ -237,7 +242,7 @@
 						if (strlen(added) + strlen(add) >= sizeof(added))
 						{
 							errno = E2BIG;
-							goto toohard;
+							return -1;
 						}
 						
 						(void)strcat(added, add);
@@ -246,16 +251,16 @@
 				}
 			}
 			*dp++ = which;
-			if (dp >= &buffer[limit])
+			if (dp >= buf_lim)
 			{
 				errno = E2BIG;
-				goto toohard;
+				return -1;
 			}
-			goto swap;
+			break;
 
 		case 'r':
-			oncol = 1;
-			goto setwhich;
+			oncol = 0;
+			break;
 
 		case 'i':
 			destcol++;
@@ -263,9 +268,6 @@
 			which++;
 			continue;
 
-		case '%':
-			goto copy;
-
 #ifdef CM_B
 		case 'B':
 			which = (which/10 << 4) + which%10;
@@ -280,13 +282,18 @@
 
 		default:
 			errno = EINVAL;
-			goto toohard;
+			return -1;
 		}
+
+		/* flip to other number... */
+		oncol = 1 - oncol;
+		which = oncol ? destcol : destline;
 	}
-	if (dp + strlen(added) >= &buffer[limit])
+
+	if (dp + strlen(added) >= buf_lim)
 	{
 		errno = E2BIG;
-		goto toohard;
+		return -1;
 	}
 
 	(void)strcpy(dp, added);
>Release-Note:
>Audit-Trail:
>Unformatted: