NetBSD Problem Report #54329

From www@netbsd.org  Thu Jun 27 05:01:06 2019
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 "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id ABFAC7A15D
	for <gnats-bugs@gnats.NetBSD.org>; Thu, 27 Jun 2019 05:01:06 +0000 (UTC)
Message-Id: <20190627050105.C22097A1DB@mollari.NetBSD.org>
Date: Thu, 27 Jun 2019 05:01:05 +0000 (UTC)
From: knz@thaumogen.net
Reply-To: knz@thaumogen.net
To: gnats-bugs@NetBSD.org
Cc: jordan@cockroachlabs.com, nikhil.benesch@gmail.com
Subject: libedit: undefined terminal behavior in terminal_move_to_line (fix included)
X-Send-Pr-Version: www-1.0

>Number:         54329
>Category:       lib
>Synopsis:       libedit: undefined terminal behavior in terminal_move_to_line (fix included)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Jun 27 05:05:00 +0000 2019
>Last-Modified:  Tue Jul 02 18:15:00 +0000 2019
>Originator:     Raphael Poss
>Release:        all
>Organization:
>Environment:
all
>Description:
The algorithm for terminal_move_to_line()
uses the DO termcap (aka cud or parm_down_cursor), which produces
undefined behavior when the cursor is on the last line. See the manual at https://www.gnu.org/software/termutils/manual/termcap-1.3/html_chapter/termcap_4.html#SEC23, which says:

    DO', UP', LE', RI'
    Strings of commands to move the cursor n lines down vertically, up
    vertically, or n columns left or right. Do not attempt to move past any
    edge of the screen with these commands; the effect of trying that is
    undefined. Only a few terminal descriptions provide these commands, and
    most programs do not use them.

A patch is included below.

(Credits to Jordan Lewis of Cockroach Labs and Nikhil Benesch for investigating this)
>How-To-Repeat:
For reviewers trying to understand why DO is not appropriate on the last line, I encourage you to try running "tput cud 5" while on the last line of your terminal versus somewhere higher up. That command outputs a DO cap with an argument of 5, which should move the cursor 5 lines down. This will work as long as the cursor doesn't hit the last line.
>Fix:
The following patch seems to help:

diff --git a/src/terminal.c b/src/terminal.c
index d8db3d1..fb865b6 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -509,15 +509,10 @@ terminal_move_to_line(EditLine *el, int where)
                return;
        }
        if ((del = where - el->el_cursor.v) > 0) {
-               if ((del > 1) && GoodStr(T_DO)) {
-                       terminal_tputs(el, tgoto(Str(T_DO), del, del), del);
-                       del = 0;
-               } else {
-                       for (; del > 0; del--)
-                               terminal__putc(el, '\n');
-                       /* because the \n will become \r\n */
-                       el->el_cursor.h = 0;
-               }
+               for (; del > 0; del--)
+                       terminal__putc(el, '\n');
+               /* because the \n will become \r\n */
+               el->el_cursor.h = 0;
        } else {                /* del < 0 */
                if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
                        terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del);

>Release-Note:

>Audit-Trail:
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Fri, 28 Jun 2019 02:09:18 +0300

 On Thu, Jun 27, 2019 at 05:05:01 +0000, knz@thaumogen.net wrote:

 > The algorithm for terminal_move_to_line() uses the DO termcap (aka
 > cud or parm_down_cursor), which produces undefined behavior when the
 > cursor is on the last line.

 Out of curiosity, is this a real problem, though?  Doesn't the code
 just above verify that "where" is within screen bounds?

 -uwe

From: Jordan Lewis <jordan@cockroachlabs.com>
To: "Raphael 'kena' Poss" <knz@thaumogen.net>, gnats-bugs@netbsd.org
Cc: Nikhil Benesch <nikhil.benesch@gmail.com>
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Fri, 28 Jun 2019 09:55:38 -0400

 --000000000000c4630f058c62a204
 Content-Type: text/plain; charset="UTF-8"

  > Out of curiosity, is this a real problem, though?  Doesn't the code
  > just above verify that "where" is within screen bounds?
  > -uwe

 The reason we started messing with this is that if you try to restore a
 history entry that's taller than 2 lines when the cursor is at the bottom
 of the screen, the program tries to use T_DO to move the cursor, which has
 no effect, so it ends up overwriting the bottom line with the contents of
 all of the lines of the multi-line history entry.

 So there is a real practical problem here. Perhaps that code is supposed to
 verify the condition you say, but it doesn't work.

 On Fri, Jun 28, 2019 at 4:14 AM Raphael 'kena' Poss <knz@thaumogen.net>
 wrote:

 >
 > please respond
 >
 > -------- Forwarded Message --------
 > Subject: Re: lib/54329: libedit: undefined terminal behavior in
 > terminal_move_to_line (fix included)
 > Date: Thu, 27 Jun 2019 23:10:01 +0000 (UTC)
 > From: Valery Ushakov <uwe@stderr.spb.ru>
 > Reply-To: gnats-bugs@netbsd.org
 > To: lib-bug-people@netbsd.org, gnats-admin@netbsd.org,
 > netbsd-bugs@netbsd.org, knz@thaumogen.net
 >
 > The following reply was made to PR lib/54329; it has been noted by GNATS.
 >
 > From: Valery Ushakov <uwe@stderr.spb.ru>
 > To: gnats-bugs@netbsd.org
 > Cc: Subject: Re: lib/54329: libedit: undefined terminal behavior in
 >  terminal_move_to_line (fix included)
 > Date: Fri, 28 Jun 2019 02:09:18 +0300
 >
 >  On Thu, Jun 27, 2019 at 05:05:01 +0000, knz@thaumogen.net wrote:
 >   > The algorithm for terminal_move_to_line() uses the DO termcap (aka
 >  > cud or parm_down_cursor), which produces undefined behavior when the
 >  > cursor is on the last line.
 >   Out of curiosity, is this a real problem, though?  Doesn't the code
 >  just above verify that "where" is within screen bounds?
 >   -uwe
 >
 >

 --000000000000c4630f058c62a204
 Content-Type: text/html; charset="UTF-8"
 Content-Transfer-Encoding: quoted-printable

 <div dir=3D"ltr"><div dir=3D"ltr"><div>=C2=A0&gt; Out of curiosity, is this=
  a real problem, though?=C2=A0 Doesn&#39;t the code<br>=C2=A0&gt; just abov=
 e verify that &quot;where&quot; is within screen bounds?<br>=C2=A0&gt; -uwe=
 <div class=3D"gmail-yj6qo"></div><br class=3D"gmail-Apple-interchange-newli=
 ne"></div><div>The reason we started messing with this is that if you try t=
 o restore a history entry that&#39;s taller than 2 lines when the cursor is=
  at the bottom of the screen, the program tries to use T_DO to move the cur=
 sor, which has no effect, so it ends up overwriting the bottom line with th=
 e contents of all of the lines of the multi-line history entry.</div><div><=
 br></div><div>So there is a real practical problem here. Perhaps that code =
 is supposed to verify the condition you say, but it doesn&#39;t work.</div>=
 </div><br><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">=
 On Fri, Jun 28, 2019 at 4:14 AM Raphael &#39;kena&#39; Poss &lt;<a href=3D"=
 mailto:knz@thaumogen.net">knz@thaumogen.net</a>&gt; wrote:<br></div><blockq=
 uote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1p=
 x solid rgb(204,204,204);padding-left:1ex"><br>
 please respond<br>
 <br>
 -------- Forwarded Message --------<br>
 Subject: Re: lib/54329: libedit: undefined terminal behavior in<br>
 terminal_move_to_line (fix included)<br>
 Date: Thu, 27 Jun 2019 23:10:01 +0000 (UTC)<br>
 From: Valery Ushakov &lt;<a href=3D"mailto:uwe@stderr.spb.ru" target=3D"_bl=
 ank">uwe@stderr.spb.ru</a>&gt;<br>
 Reply-To: <a href=3D"mailto:gnats-bugs@netbsd.org" target=3D"_blank">gnats-=
 bugs@netbsd.org</a><br>
 To: <a href=3D"mailto:lib-bug-people@netbsd.org" target=3D"_blank">lib-bug-=
 people@netbsd.org</a>, <a href=3D"mailto:gnats-admin@netbsd.org" target=3D"=
 _blank">gnats-admin@netbsd.org</a>,<br>
 <a href=3D"mailto:netbsd-bugs@netbsd.org" target=3D"_blank">netbsd-bugs@net=
 bsd.org</a>, <a href=3D"mailto:knz@thaumogen.net" target=3D"_blank">knz@tha=
 umogen.net</a><br>
 <br>
 The following reply was made to PR lib/54329; it has been noted by GNATS.<b=
 r>
 <br>
 From: Valery Ushakov &lt;<a href=3D"mailto:uwe@stderr.spb.ru" target=3D"_bl=
 ank">uwe@stderr.spb.ru</a>&gt;<br>
 To: <a href=3D"mailto:gnats-bugs@netbsd.org" target=3D"_blank">gnats-bugs@n=
 etbsd.org</a><br>
 Cc: Subject: Re: lib/54329: libedit: undefined terminal behavior in<br>
 =C2=A0terminal_move_to_line (fix included)<br>
 Date: Fri, 28 Jun 2019 02:09:18 +0300<br>
 <br>
 =C2=A0On Thu, Jun 27, 2019 at 05:05:01 +0000, <a href=3D"mailto:knz@thaumog=
 en.net" target=3D"_blank">knz@thaumogen.net</a> wrote:<br>
 =C2=A0 &gt; The algorithm for terminal_move_to_line() uses the DO termcap (=
 aka<br>
 =C2=A0&gt; cud or parm_down_cursor), which produces undefined behavior when=
  the<br>
 =C2=A0&gt; cursor is on the last line.<br>
 =C2=A0 Out of curiosity, is this a real problem, though?=C2=A0 Doesn&#39;t =
 the code<br>
 =C2=A0just above verify that &quot;where&quot; is within screen bounds?<br>
 =C2=A0 -uwe<br>
 <br>
 </blockquote></div></div>

 --000000000000c4630f058c62a204--

From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: lib-bug-people@netbsd.org,
 gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org,
 knz@thaumogen.net
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Fri, 28 Jun 2019 20:56:20 -0400

 What terminal is that? I am asking because xterm works....

 christos

From: Raphael 'kena' Poss <knz@thaumogen.net>
To: Jordan Lewis <jordan@cockroachlabs.com>, gnats-bugs@netbsd.org
Cc: Nikhil Benesch <nikhil.benesch@gmail.com>
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Sat, 29 Jun 2019 09:05:46 +0200

 On 28-06-19 15:55, Jordan Lewis wrote:
 > The reason we started messing with this is that if you try to restore a
 > history entry that's taller than 2 lines when the cursor is at the
 > bottom of the screen, the program tries to use T_DO to move the cursor,
 > which has no effect, so it ends up overwriting the bottom line with the
 > contents of all of the lines of the multi-line history entry.

 For reference I have tried this (recalling 2 lines of history at the
 bottom of the terminal, with libedit) on the following terminals:

 - st
 - lxterm
 - xfce4-terminal
 - xterm

 and I observe no issue whatsoever. also used `tput cud 5` on the last
 line and observed no badness.

 Like the other commenter, I suspect you have a different problem.
 (Or perhaps you need to record your screen to show more precisely what
 is happening)

 I would recommend checking the following:

 - that the TERM env var is set properly for your terminal. Possibly you
 need to change it.
 - if you are using `screen` or `tmux`, bring it up to date.
 - if you are using `screen` or `tmux`, ensure BOTH that:
   - the TERM env var *outside* of the session manager matches your
 terminal emulator.
   - the TERM env var *inside* of the session manager is properly set to,
 respectively, "screen" or "tmux".


 -- 
 Raphael 'kena' Poss

From: Jordan Lewis <jordan@cockroachlabs.com>
To: "Raphael 'kena' Poss" <knz@thaumogen.net>
Cc: gnats-bugs@netbsd.org, Nikhil Benesch <nikhil.benesch@gmail.com>
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Sat, 29 Jun 2019 10:05:02 -0400

 --0000000000005042eb058c76e25f
 Content-Type: text/plain; charset="UTF-8"

 On Sat, Jun 29, 2019, 03:05 Raphael 'kena' Poss <knz@thaumogen.net> wrote:

 >
 > For reference I have tried this (recalling 2 lines of history at the
 > bottom of the terminal, with libedit) on the following terminals:
 >
 > - st
 > - lxterm
 > - xfce4-terminal
 > - xterm
 >
 > and I observe no issue whatsoever. also used `tput cud 5` on the last
 > line and observed no badness.


 It needs to be at least 3 lines of history. With 2 lines of history, the
 program uses newlines, not T_DO.

 Regardless of whether some terminals handle this ok, the old behavior works
 only by accident. The manual is very clear that T_DO at the bottom of the
 screen results in undefined behavior.

 --0000000000005042eb058c76e25f
 Content-Type: text/html; charset="UTF-8"
 Content-Transfer-Encoding: quoted-printable

 <div dir=3D"auto"><div><div class=3D"gmail_quote"><div dir=3D"ltr" class=3D=
 "gmail_attr">On Sat, Jun 29, 2019, 03:05 Raphael &#39;kena&#39; Poss &lt;<a=
  href=3D"mailto:knz@thaumogen.net">knz@thaumogen.net</a>&gt; wrote:</div><b=
 lockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px =
 #ccc solid;padding-left:1ex">
 <br>
 For reference I have tried this (recalling 2 lines of history at the<br>
 bottom of the terminal, with libedit) on the following terminals:<br>
 <br>
 - st<br>
 - lxterm<br>
 - xfce4-terminal<br>
 - xterm<br>
 <br>
 and I observe no issue whatsoever. also used `tput cud 5` on the last<br>
 line and observed no badness.</blockquote></div></div><div dir=3D"auto"><br=
 ></div><div dir=3D"auto">It needs to be at least 3 lines of history. With 2=
  lines of history, the program uses newlines, not T_DO.</div><div dir=3D"au=
 to"><br></div><div dir=3D"auto">Regardless of whether some terminals handle=
  this ok, the old behavior works only by accident. The manual is very clear=
  that T_DO at the bottom of the screen results in undefined behavior.</div>=
 <div dir=3D"auto"></div></div>

 --0000000000005042eb058c76e25f--

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/54329 CVS commit: src/lib/libedit
Date: Sat, 29 Jun 2019 17:35:09 -0400

 Module Name:	src
 Committed By:	christos
 Date:		Sat Jun 29 21:35:09 UTC 2019

 Modified Files:
 	src/lib/libedit: terminal.c

 Log Message:
 PR/54329: Raphael Ross: According to https://www.gnu.org/software/termutils/\
 manual/termcap-1.3/html_chapter/termcap_4.html#SEC23 the cursor move multiple
 escapes have undefined results when moving out of the screen. Stop using DO
 to move down multiple lines and use a loop of newlines instead.


 To generate a diff of this commit:
 cvs rdiff -u -r1.36 -r1.37 src/lib/libedit/terminal.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc: Christos Zoulas <christos@zoulas.com>
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Sun, 30 Jun 2019 14:34:50 +0300

 On Sat, Jun 29, 2019 at 14:10:01 +0000, Jordan Lewis wrote:

 >  Regardless of whether some terminals handle this ok, the old behavior works
 >  only by accident. The manual is very clear that T_DO at the bottom of the
 >  screen results in undefined behavior.

 But the code just above is also very clearly checking for that, isn't
 it?  It may be getting confused about the window size and end up using
 DO when it shouldn't, but then it's the size tracking that should be
 fixed (as it likely has other bad consequences), not the innocent
 bystander.

 Also, as far as I can tell you still haven't provided complete
 reproduction scenario.  The emulator you use, window size, the value
 of TERM, etc.

 Christos, were you able to reproduce this?  Yyou said you didn't and
 then suddenly applied the diff.  This is confusing.

 -uwe

From: Christos Zoulas <christos@zoulas.com>
To: Valery Ushakov <uwe@stderr.spb.ru>
Cc: gnats-bugs@netbsd.org
Subject: Re: lib/54329: libedit: undefined terminal behavior in
 terminal_move_to_line (fix included)
Date: Sun, 30 Jun 2019 09:23:24 -0400

 I could not reproduce it, but I've seen it before. In the end the =
 optimization is probably not worth the complexity.
 This code was written 30 years ago where the cost of each character was =
 high at 2400 or 9600 baud. It is also
 unclear that the cost/benefit warrants the complexity in the frequently =
 used cases. For example on an xterm,
 the DO sequence is "\e[%p1%dB" which would take quite a few lines (> 5 =
 if I counted right with 0 padding), to
 exceed the cost of just printing line feeds. So unless am I missing =
 something, we don't lose much by skipping
 the optimization.

 christos=

From: Nikhil Benesch <nikhil.benesch@gmail.com>
To: gnats-bugs@netbsd.org
Cc: uwe@stderr.spb.ru,
 jordan@cockroachlabs.com,
 knz@thaumogen.net,
 christos@zoulas.com
Subject: Re: lib/54329
Date: Mon, 1 Jul 2019 17:32:45 -0400

 I made a video reproducing this bug using xterm on NetBSD 8.0:

     https://www.youtube.com/watch?v=3D_11p93_ylyk

 Note that the tc1 program shown in the video was compiled with Jess =
 Thrysoee's
 autotoolized libedit port, v20190324-3.1. (I don't have the NetBSD-foo =
 to
 upgrade the bundled libedit, but I can learn and try to upgrade my VM to
 the latest trunk, if someone thinks that's important.)

 It's not shown in the video, but $TERM is set to "xterm".

 Responses to a few specific points in line below:

 > ...also used `tput cud 5` on the last line and observed no badness.

 It's not that badness happens if you run `tput cud 5` with the cursor =
 positioned
 on the last line; it's that nothing happens. This is demonstrated in the =
 video.
 The result is that libedit's cursor position tracking gets out of sync =
 with the
 cursor's actual vertical position; libedit thinks that the cursor has =
 been moved
 down five lines when, in fact, it has not.

 > But the code just above is also very clearly checking for that, isn't
 > it?  It may be getting confused about the window size and end up using
 > DO when it shouldn't, but then it's the size tracking that should be
 > fixed (as it likely has other bad consequences), not the innocent
 > bystander.

 Based on my debugging, libedit is not tracking the absolute vertical =
 position of
 the cursor. It is tracking only its relative vertical position, relative =
 to
 wherever the cursor is when the editline instance is initialized. So if =
 the
 editing session is started when the cursor is already on the last line, =
 the
 editline.el_cursor.v will be set to "0", which tells us nothing about =
 whether it
 is safe to send T_DO or not.

 It's possible that the absolute vertical position is tracked somewhere, =
 but
 nowhere that I've found. Perhaps it would be easy enough to add, but I
 concur with Christos that the optimization is unlikely to be worthwhile =
 on
 modern terminal emulators.

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: lib/54329
Date: Tue, 2 Jul 2019 18:14:05 +0000

 On Mon, Jul 01, 2019 at 09:35:02PM +0000, Nikhil Benesch wrote:
  >  Based on my debugging, libedit is not tracking the absolute vertical =
  >  position of
  >  the cursor. It is tracking only its relative vertical position, relative =
  >  to
  >  wherever the cursor is when the editline instance is initialized. So if =
  >  the
  >  editing session is started when the cursor is already on the last line, =
  >  the
  >  editline.el_cursor.v will be set to "0", which tells us nothing about =
  >  whether it
  >  is safe to send T_DO or not.

 This appears to be the case, inasmuch as nothing ever assigns
 el_cursor.v, only adjusts it, but I still can't reproduce the problem.

 If you start this as your terminal session, e.g. with xterm -e:

 #!/bin/sh
 for x in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do echo $x; done
 exec sh -o emacs

 the libedit in sh should initialize a long way from the top of the
 screen, but I don't see the reported behavior in either xterm or tmux,
 even though both do nothing when you run tput DO 5 on the bottom line.
 (With a libedit binary from November.)

  >  It's possible that the absolute vertical position is tracked somewhere, =
  >  but
  >  nowhere that I've found. Perhaps it would be easy enough to add, but I
  >  concur with Christos that the optimization is unlikely to be worthwhile =
  >  on
  >  modern terminal emulators.

 I'd expect other things to mess up if it's off. Even if it never
 issues an absolute cursor move, there are still things like special
 treatment of the last character on the last line to go wrong. So it
 should really be reading the cursor position from the terminal at
 initialization.

 I think I've discovered one, which is that when started this way, if
 you make an input line that's nearly as large as the whole screen,
 move to the middle of it and hold down 'a' to start inserting
 characters, when the beginning of the line scrolls off the top of the
 screen the cursor doesn't move with it. But it doesn't seem to happen
 if started normally. I think. This is presumably from the overflow
 code in re_fastputc in refresh.c, which tests el_cursor.v against
 t_size.v...

 Also, very broken things happen if you cause libedit to initialize
 when it's not at column 1.


 (hi Nikhil!)
 -- 
 David A. Holland
 dholland@netbsd.org

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.43 2018/01/16 07:36:43 maya Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2017 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.