NetBSD Problem Report #56173

From www@netbsd.org  Sat May 15 04:10:58 2021
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_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 7AEED1A9287
	for <gnats-bugs@gnats.NetBSD.org>; Sat, 15 May 2021 04:10:58 +0000 (UTC)
Message-Id: <20210515041056.EAD881A9288@mollari.NetBSD.org>
Date: Sat, 15 May 2021 04:10:56 +0000 (UTC)
From: mforney@mforney.org
Reply-To: mforney@mforney.org
To: gnats-bugs@NetBSD.org
Subject: libcurses: reset_prog_mode() after reset_shell_mode() overwrites original TTY state
X-Send-Pr-Version: www-1.0

>Number:         56173
>Category:       lib
>Synopsis:       libcurses: reset_prog_mode() after reset_shell_mode() overwrites original TTY state
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat May 15 04:15:00 +0000 2021
>Last-Modified:  Tue Jun 01 01:05:01 +0000 2021
>Originator:     Michael Forney
>Release:        trunk
>Organization:
>Environment:
>Description:
If a curses program calls reset_shell_mode() and then reset_prog_mode() to temporarily switch to shell mode and then back to curses mode, the original TTY settings are overwritten. This results in incorrect TTY settings after the application exits.

This pattern is used in the text editor vis when you read output from a shell command.

The problem seems to be that reset_shell_mode() calls __stopwin() to save the curses mode settings and restore the shell mode settings, but reset_prog_mode() just restores the saved curses mode settings, leaving the screen in the "endwin" state. Then, when the application performs a refresh(), __restartwin() is called which saves the current (already restored) settings as the original settings, and then restores the curses mode settings again. At this point, the original settings have been overwritten with the curses mode settings, which persist even after the application calls endwin() and exits.

In other words, the following sequence of events occurs:

	reset_shell_mode()
		__stopwin()
			saved settings = current settings
			current settings = original settings
			endwin = 1
	reset_prog_mode()
		current settings = saved settings
	refresh()
		__restartwin()
			original settings = current settings
			current settings = saved settings

At this point we have original settings == current settings == curses mode settings. The original settings have been overwritten.

It appears that this behavior dates back to this commit from 2003:
https://github.com/NetBSD/src/commit/880234af7eb36a86d6adfa775327ccec592d519d#diff-3825aa201b54dbfe36d66fe3f650737571d39e3459b936691d1c955f37871d2dL199-R204

Previously, reset_shell_mode() and reset_prog_mode() were symmetrical, calling __endwin() and __restartwin() respectively. After the change, reset_shell_mode() still calls __endwin(), but reset_prog_mode() just changes the TTY settings leaving the screen in the endwin state.
>How-To-Repeat:
1. Build vis (https://github.com/martanne/vis) against NetBSD libcurses, then run it.
2. Type ":<echo hello" to read the output of a command into the buffer.
3. Exit vis with ":q!".
4. Terminal settings are now messed up.
>Fix:
I don't quite understand what problem the commit referenced above was attempting to fix, but if I revert the highlighted hunk, the problem is fixed.

diff --git a/lib/libcurses/tstp.c b/lib/libcurses/tstp.c
index f0baa999120b..ef54143a4a54 100644
--- a/lib/libcurses/tstp.c
+++ b/lib/libcurses/tstp.c
@@ -348,8 +348,8 @@ int
 reset_prog_mode(void)
 {

-	return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | TCSADRAIN,
-			 &_cursesi_screen->save_termios) ? ERR : OK;
+	__restartwin();
+	return OK;
 }

 int

>Audit-Trail:
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
Subject: Re: lib/56173: libcurses: reset_prog_mode() after reset_shell_mode()
 overwrites original TTY state
Date: Mon, 31 May 2021 13:07:09 -0400

 --Apple-Mail=_6F304CAD-4C8E-4B9A-8419-26EA703F25C3
 Content-Transfer-Encoding: quoted-printable
 Content-Type: text/plain;
 	charset=us-ascii

 This program behaves very differently under ncurses and curses:

 include <stdio.h>
 #include <curses.h>
 #include <stdlib.h>

 int
 main(void)
 {
         printf("\r\ninitially\n");
         system("stty -a");
         initscr();
         printf("\r\nafter initscr\n");
         system("stty -a");
         reset_shell_mode();
         printf("\r\nafter reset_shell_mode\n");
         system("stty -a");
         reset_prog_mode();
         printf("\r\nafter reset_prog_mode\n");
         system("stty -a");
         endwin();
         printf("\r\nafter endwin\n");
         system("stty -a");
         return 0;
 }

 The question is who is right?

 christos

 > On May 15, 2021, at 12:15 AM, mforney@mforney.org wrote:
 >=20
 >> Number:         56173
 >> Category:       lib
 >> Synopsis:       libcurses: reset_prog_mode() after reset_shell_mode() =
 overwrites original TTY state
 >> Confidential:   no
 >> Severity:       serious
 >> Priority:       medium
 >> Responsible:    lib-bug-people
 >> State:          open
 >> Class:          sw-bug
 >> Submitter-Id:   net
 >> Arrival-Date:   Sat May 15 04:15:00 +0000 2021
 >> Originator:     Michael Forney
 >> Release:        trunk
 >> Organization:
 >> Environment:
 >> Description:
 > If a curses program calls reset_shell_mode() and then =
 reset_prog_mode() to temporarily switch to shell mode and then back to =
 curses mode, the original TTY settings are overwritten. This results in =
 incorrect TTY settings after the application exits.
 >=20
 > This pattern is used in the text editor vis when you read output from =
 a shell command.
 >=20
 > The problem seems to be that reset_shell_mode() calls __stopwin() to =
 save the curses mode settings and restore the shell mode settings, but =
 reset_prog_mode() just restores the saved curses mode settings, leaving =
 the screen in the "endwin" state. Then, when the application performs a =
 refresh(), __restartwin() is called which saves the current (already =
 restored) settings as the original settings, and then restores the =
 curses mode settings again. At this point, the original settings have =
 been overwritten with the curses mode settings, which persist even after =
 the application calls endwin() and exits.
 >=20
 > In other words, the following sequence of events occurs:
 >=20
 > 	reset_shell_mode()
 > 		__stopwin()
 > 			saved settings =3D current settings
 > 			current settings =3D original settings
 > 			endwin =3D 1
 > 	reset_prog_mode()
 > 		current settings =3D saved settings
 > 	refresh()
 > 		__restartwin()
 > 			original settings =3D current settings
 > 			current settings =3D saved settings
 >=20
 > At this point we have original settings =3D=3D current settings =3D=3D =
 curses mode settings. The original settings have been overwritten.
 >=20
 > It appears that this behavior dates back to this commit from 2003:
 > =
 https://github.com/NetBSD/src/commit/880234af7eb36a86d6adfa775327ccec592d5=
 19d#diff-3825aa201b54dbfe36d66fe3f650737571d39e3459b936691d1c955f37871d2dL=
 199-R204
 >=20
 > Previously, reset_shell_mode() and reset_prog_mode() were symmetrical, =
 calling __endwin() and __restartwin() respectively. After the change, =
 reset_shell_mode() still calls __endwin(), but reset_prog_mode() just =
 changes the TTY settings leaving the screen in the endwin state.
 >> How-To-Repeat:
 > 1. Build vis (https://github.com/martanne/vis) against NetBSD =
 libcurses, then run it.
 > 2. Type ":<echo hello" to read the output of a command into the =
 buffer.
 > 3. Exit vis with ":q!".
 > 4. Terminal settings are now messed up.
 >> Fix:
 > I don't quite understand what problem the commit referenced above was =
 attempting to fix, but if I revert the highlighted hunk, the problem is =
 fixed.
 >=20
 > diff --git a/lib/libcurses/tstp.c b/lib/libcurses/tstp.c
 > index f0baa999120b..ef54143a4a54 100644
 > --- a/lib/libcurses/tstp.c
 > +++ b/lib/libcurses/tstp.c
 > @@ -348,8 +348,8 @@ int
 > reset_prog_mode(void)
 > {
 >=20
 > -	return tcsetattr(fileno(_cursesi_screen->infd), TCSASOFT | =
 TCSADRAIN,
 > -			 &_cursesi_screen->save_termios) ? ERR : OK;
 > +	__restartwin();
 > +	return OK;
 > }
 >=20
 > int


 --Apple-Mail=_6F304CAD-4C8E-4B9A-8419-26EA703F25C3
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCYLUXvQAKCRBxESqxbLM7
 OgDzAJ4ulhf4OsPZOcIHyjSKH7O1HGY2bACg4cqhYJXyUIGVEA8bACdHAZpFM70=
 =od53
 -----END PGP SIGNATURE-----

 --Apple-Mail=_6F304CAD-4C8E-4B9A-8419-26EA703F25C3--

From: Brett Lymn <blymn@internode.on.net>
To: gnats-bugs@netbsd.org
Cc: lib-bug-people@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
        mforney@mforney.org
Subject: Re: lib/56173: libcurses: reset_prog_mode() after reset_shell_mode()
 overwrites original TTY state
Date: Tue, 1 Jun 2021 10:30:56 +0930

 On Mon, May 31, 2021 at 05:10:02PM +0000, Christos Zoulas wrote:
 >  
 >  The question is who is right?
 >  

 Unfortunately, I don't think it matters who is more correct - if the
 behaviour is not defined clearly in the standard then we probably should
 just follow ncurses because that is what most code expects.

 -- 
 Brett Lymn
 --
 Sent from my NetBSD device.

 "We are were wolves",
 "You mean werewolves?",
 "No we were wolves, now we are something else entirely",
 "Oh"

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.46 2020/01/03 16:35:01 leot Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2020 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.