NetBSD Problem Report #51986

From www@NetBSD.org  Sun Feb 19 18:47:27 2017
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 911C37A223
	for <gnats-bugs@gnats.NetBSD.org>; Sun, 19 Feb 2017 18:47:27 +0000 (UTC)
Message-Id: <20170219184726.056807A2A9@mollari.NetBSD.org>
Date: Sun, 19 Feb 2017 18:47:25 +0000 (UTC)
From: satoshi.kanemitsu@gmail.com
Reply-To: satoshi.kanemitsu@gmail.com
To: gnats-bugs@NetBSD.org
Subject: libedit: strange behavior caused by a combination of scrolling and line wrapping
X-Send-Pr-Version: www-1.0

>Number:         51986
>Category:       lib
>Synopsis:       libedit: strange behavior caused by a combination of scrolling and line wrapping
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Feb 19 18:50:00 +0000 2017
>Originator:     Satoshi Kanemitsu
>Release:        libedit (NetBSD-current)
>Organization:
>Environment:
NetBSD localhost 7.0.2 NetBSD 7.0.2 (GENERIC.201610210724Z) amd64
>Description:
libedit(/bin/sh) crashes or exhibits strange behavior when using the virtical scrolling and line wrapping at the same time.
It seems that the problem is related to the following factors.

 1. Array index is out of bounds in some cases (It will cause a segmentation fault).
 2. The screen does not refresh if the cursor goes off-screen.
 3. libedit can not get the right cursor position when the prompt disappears.

>How-To-Repeat:
1. Typing a long line (more than terminal size).
2. Press enter.
3. Press arrow-up (the prompt disappears).
4. Press arrow-left repeatedly (the cursor moves to the wrong position).
5. Segmentation fault.

>Fix:
I propose a following patch.

--- libedit/refresh.c.orig	2016-05-10 12:01:03.000000000 +0900
+++ libedit/refresh.c	2017-02-12 12:48:25.000000000 +0900
@@ -61,2 +61,3 @@ static void	re__strncopy(wchar_t *, wcha
 static void	re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
+static void	re_get_nextposition(EditLine *, wint_t, coord_t *);

@@ -113,2 +114,4 @@ re_nextline(EditLine *el)
 		el->el_vdisplay[i - 1] = firstline;
+
+		el->el_prompt.p_pos.v--;
 	} else
@@ -266,2 +269,18 @@ re_refresh(EditLine *el)
 		}
+		if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v &&
+		    cur.h >= 0) {
+			coord_t pos;
+
+			pos.h = el->el_refresh.r_cursor.h;
+			pos.v = el->el_refresh.r_cursor.v;
+			re_get_nextposition(el, *cp, &pos);
+			if (pos.v > el->el_refresh.r_cursor.v) {
+				cur.v--;
+				/* keep the cursor on the screen */
+				if (cur.v < 0) {
+					cur.v = 0;
+					break;
+				}
+			}
+		}
 		re_addc(el, *cp);
@@ -998,3 +1017,4 @@ re_refresh_cursor(EditLine *el)
 	wchar_t *cp;
-	int h, v, th, w;
+	coord_t cur;
+	int w;

@@ -1009,32 +1029,9 @@ re_refresh_cursor(EditLine *el)
 	/* first we must find where the cursor is... */
-	h = el->el_prompt.p_pos.h;
-	v = el->el_prompt.p_pos.v;
-	th = el->el_terminal.t_size.h;	/* optimize for speed */
+	cur.h = el->el_prompt.p_pos.h;
+	cur.v = el->el_prompt.p_pos.v;	/* negative value means "off-screen" */

 	/* do input buffer to el->el_line.cursor */
-	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
-                switch (ct_chr_class(*cp)) {
-		case CHTYPE_NL:  /* handle newline in data part too */
-			h = 0;
-			v++;
-			break;
-		case CHTYPE_TAB: /* if a tab, to next tab stop */
-			while (++h & 07)
-				continue;
-			break;
-		default:
-			w = wcwidth(*cp);
-			if (w > 1 && h + w > th) { /* won't fit on line */
-				h = 0;
-				v++;
-			}
-			h += ct_visual_width(*cp);
-			break;
-                }
+	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++)
+		re_get_nextposition(el, *cp, &cur);

-		if (h >= th) {	/* check, extra long tabs picked up here also */
-			h -= th;
-			v++;
-		}
-	}
         /* if we have a next character, and it's a doublewidth one, we need to
@@ -1042,10 +1039,15 @@ re_refresh_cursor(EditLine *el)
         if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
-                if (h + w > th) {
-                    h = 0;
-                    v++;
+                if (cur.h + w > el->el_terminal.t_size.h) {
+                    cur.h = 0;
+                    cur.v++;
                 }

+	if (cur.v < 0 || cur.v >= el->el_terminal.t_size.v) {
+		re_refresh(el);
+		return;
+	}
+
 	/* now go there */
-	terminal_move_to_line(el, v);
-	terminal_move_to_char(el, h);
+	terminal_move_to_line(el, cur.v);
+	terminal_move_to_char(el, cur.h);
 	terminal__flush(el);
@@ -1089,2 +1091,4 @@ re_fastputc(EditLine *el, wint_t c)
 			el->el_display[i - 1] = firstline;
+
+			el->el_prompt.p_pos.v--;
 		} else {
@@ -1187 +1191,34 @@ re_clear_lines(EditLine *el)
 }
+
+
+/* re_get_nextposition():
+ *	Get the next position
+ */
+static void re_get_nextposition(EditLine * el, wint_t c, coord_t *pos)
+{
+	int th, w;
+
+	th = el->el_terminal.t_size.h;
+	switch (ct_chr_class(c)) {
+	case CHTYPE_TAB:	/* if a tab, to next tab stop */
+		while (++pos->h & 07)
+			continue;
+		break;
+	case CHTYPE_NL:
+		pos->h = 0;
+		pos->v++;
+		break;
+	default:
+		w = wcwidth(c);
+		if (w > 1 && pos->h + w > th) { /* won't fit on line */
+			pos->h = 0;
+			pos->v++;
+		}
+		pos->h += ct_visual_width(c);
+		break;
+	}
+	if (pos->h >= th) {	/* check, extra long tabs picked up here also */
+		pos->h -= th;
+		pos->v++;
+	}
+}
--- libedit/terminal.c.orig	2016-05-10 12:01:03.000000000 +0900
+++ libedit/terminal.c	2017-02-08 15:20:41.000000000 +0900
@@ -506,3 +506,3 @@ terminal_move_to_line(EditLine *el, int 

-	if (where > el->el_terminal.t_size.v) {
+	if (where >= el->el_terminal.t_size.v) {
 #ifdef DEBUG_SCREEN
@@ -570,3 +570,3 @@ mc_again:

-	if (where > el->el_terminal.t_size.h) {
+	if (where >= el->el_terminal.t_size.h) {
 #ifdef DEBUG_SCREEN
@@ -679,3 +679,4 @@ terminal_overwrite(EditLine *el, const w
 			el->el_cursor.h = 0;
-			el->el_cursor.v++;
+			if (el->el_cursor.v + 1 < el->el_terminal.t_size.v)
+				el->el_cursor.v++;
 			if (EL_HAS_MAGIC_MARGINS) {

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.