From jdc@koitsu.dyndns.org  Mon Oct 17 11:55:36 2011
Return-Path: <jdc@koitsu.dyndns.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 69900106564A
	for <freebsd-gnats-submit@freebsd.org>; Mon, 17 Oct 2011 11:55:36 +0000 (UTC)
	(envelope-from jdc@koitsu.dyndns.org)
Received: from qmta09.emeryville.ca.mail.comcast.net (qmta09.emeryville.ca.mail.comcast.net [76.96.30.96])
	by mx1.freebsd.org (Postfix) with ESMTP id 54D908FC0A
	for <freebsd-gnats-submit@freebsd.org>; Mon, 17 Oct 2011 11:55:36 +0000 (UTC)
Received: from omta23.emeryville.ca.mail.comcast.net ([76.96.30.90])
	by qmta09.emeryville.ca.mail.comcast.net with comcast
	id lnvV1h0011wfjNsA9nvV7R; Mon, 17 Oct 2011 11:55:29 +0000
Received: from koitsu.dyndns.org ([67.180.84.87])
	by omta23.emeryville.ca.mail.comcast.net with comcast
	id lnu51h00V1t3BNj8jnu5kB; Mon, 17 Oct 2011 11:54:05 +0000
Received: by icarus.home.lan (Postfix, from userid 1000)
	id EB277102C1C; Mon, 17 Oct 2011 04:55:34 -0700 (PDT)
Message-Id: <20111017115534.EB277102C1C@icarus.home.lan>
Date: Mon, 17 Oct 2011 04:55:34 -0700 (PDT)
From: Jeremy Chadwick <freebsd@jdc.parodius.com>
Reply-To: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: top -b does not restore ICANON and ECHO terminal capabilities when exiting
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         161739
>Category:       bin
>Synopsis:       top(1): top -b does not restore ICANON and ECHO terminal capabilities when exiting
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kib
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Oct 17 12:00:24 UTC 2011
>Closed-Date:    Wed Apr 25 05:24:43 UTC 2012
>Last-Modified:  Wed Apr 25 05:24:43 UTC 2012
>Originator:     Jeremy Chadwick
>Release:        FreeBSD 8.2-STABLE amd64
>Organization:
>Environment:
System: FreeBSD icarus.home.lan 8.2-STABLE FreeBSD 8.2-STABLE #0: Wed Sep 28 05:02:05 PDT 2011 root@icarus.home.lan:/usr/obj/usr/src/sys/X7SBA_RELENG_8_amd64 amd64
>Description:
	It appears that "top -b" results in top disabling ICANON and
	ECHO the controlling terminal/tty, but upon exit never
	re-enables them.

	I can confirm this problem on RELENG_7 (7.4-STABLE) and RELENG_8
	(8.2-STABLE).  "top -b" does not work on RELENG_6.

	stty -a after running top -b shows "-icanon" and "-echo", which
	confirms the bug.

	I have not looked at the top code to find out where the bug
	is, but I do see calls which adjust ICANON and ECHO capabilities
	on the tty, so it's definitely there.
>How-To-Repeat:
	1. Run top -b
	2. Attempt to type something into your shell or make use of an
	app which relies on canonical input.
>Fix:
	Manually typing "stty echo icanon" after using top -b will
	restore proper bits on the tty.

>Release-Note:
>Audit-Trail:

From: Alexander Best <arundel@freebsd.org>
To: Jeremy Chadwick <freebsd@jdc.parodius.com>
Cc: FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/161739: top -b does not restore ICANON and ECHO terminal capabilities when exiting
Date: Mon, 17 Oct 2011 12:08:13 +0000

 i can also confirm this running a very recent HEAD:
 
 otaku% stty -a > start; top -b ; stty -a > stop ; diff start stop
 
 [...]
 
 2c2
 < lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl
 ---
 > lflags: -icanon isig iexten -echo echoe echok echoke -echonl echoctl
 
 cheers.
 alex
 
From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: FreeBSD-gnats-submit@FreeBSD.org, freebsd-bugs@FreeBSD.org
Cc:  
Subject: Re: bin/161739: top -b does not restore ICANON and ECHO terminal
 capabilities when exiting
Date: Mon, 17 Oct 2011 11:55:42 -0700

 Follow-up:
 
 Something I was thinking about as I was falling asleep last night:
 there's a good possibility the problem is actually reversed.  That is to
 say, ICANON/ECHO is being adjusted when in "top -b" mode it shouldn't
 be.  If this is the case, some simple if()s should do the trick.
 
 -- 
 | Jeremy Chadwick                                jdc at parodius.com |
 | Parodius Networking                       http://www.parodius.com/ |
 | UNIX Systems Administrator                   Mountain View, CA, US |
 | Making life hard for others since 1977.               PGP 4BD6C0CB |
 

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Mon, 16 Apr 2012 14:46:09 -0700

 This issue has now begun to affect "top -a" as well.  I just deployed a
 new RELENG_8 box yesterday which has this same problem when using "top
 -a".  So it's no longer limited to just the -b flag.
 
 I do not know who/what touched code that caused this new behaviour.
 
 RELENG_8 from 2012/02/10 only behaves this way with -b
 RELENG_8 from 2012/04/15 behaves this way with -b and -a
 
 Please increase the priority from medium to high as a result.
 
 -- 
 | Jeremy Chadwick                                 jdc@parodius.com |
 | Parodius Networking                     http://www.parodius.com/ |
 | UNIX Systems Administrator                 Mountain View, CA, US |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |
 

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Fwd: Re: Fwd: Re: bin/161739: top(1): top -b does not restore ICANON
 and ECHO terminal capabilities when exiting
Date: Tue, 17 Apr 2012 10:40:04 -0700

 Below is an Email I sent kib@ about this problem, as he said he could
 not reproduce it.
 
 I spent 3 hours on this today, writing a debugging routine to print out
 all the internal variables and the terminal structures (termios) to
 find out where the problem lies.
 
 It appears that now the "-b" flag works fine, but "-a" is now broken
 as a result of that commit.
 
 The explanation for what's happening in the code is below.  Someone just
 needs to figure out why is_a_terminal becomes 0 during the program's
 execution (printing output, etc.) because this is what causes the issue.
 
 -- 
 | Jeremy Chadwick                                 jdc@parodius.com |
 | Parodius Networking                     http://www.parodius.com/ |
 | UNIX Systems Administrator                 Mountain View, CA, US |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |
 
 ----- Forwarded message from Jeremy Chadwick <freebsd@jdc.parodius.com> -----
 
 > From: Jeremy Chadwick <freebsd@jdc.parodius.com>
 > To: Konstantin Belousov <kostikbel@gmail.com>
 > Date: Tue, 17 Apr 2012 10:29:33 -0700
 > Subject: Re: Fwd: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO terminal capabilities when exiting
 > 
 > I stand partially corrected -- it looks like your change made recently
 > fixed the problem in the PR for the -b flag only; but now -a has the
 > problem.
 > 
 > I wrote a debug printing routine and shoved it into top.c to look at the
 > results of new_settings and old_settings (termios struct), and also what
 > the current terminal settings are for comparison and some other internal
 > variables which are used to determine when/how to reset the terminal.
 > 
 > There is definitely a problem.
 > 
 > (10:23:01 jdc@omake) ~/usr.bin/top $ ./top -a 2>/tmp/results
 > 
 > {pressed "q" after 1 iteration}
 > 
 > (10:23:18 jdc@omake) ~/usr.bin/top $ (10:23:24 jdc@omake) ~/usr.bin/top $
 > 
 > {I had to type "stty icanon echo" to restore things}
 > 
 > Results:
 > 
 > (10:23:26 jdc@omake) ~/usr.bin/top $ cat /tmp/results
 > DEBUG: before init_termcap
 > DEBUG: is_a_terminal  = 0
 > DEBUG: smart_terminal = 0
 > DEBUG: interactive    = 2 (Maybe)
 > DEBUG: displays       = 0
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x0
 > DEBUG:   c_oflag = 0x0
 > DEBUG:   c_cflag = 0x0
 > DEBUG:   c_lflag = 0x0 ()
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x0
 > DEBUG:   c_oflag = 0x0
 > DEBUG:   c_cflag = 0x0
 > DEBUG:   c_lflag = 0x0 ()
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > ----------------------------------------
 > DEBUG: after init_termcap
 > DEBUG: is_a_terminal  = 0
 > DEBUG: smart_terminal = 1
 > DEBUG: interactive    = 2 (Maybe)
 > DEBUG: displays       = 0
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x0
 > DEBUG:   c_oflag = 0x0
 > DEBUG:   c_cflag = 0x0
 > DEBUG:   c_lflag = 0x0 ()
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > ----------------------------------------
 > DEBUG: before init_screen
 > DEBUG: is_a_terminal  = 0
 > DEBUG: smart_terminal = 1
 > DEBUG: interactive    = 1 (Yes)
 > DEBUG: displays       = -1
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x0
 > DEBUG:   c_oflag = 0x0
 > DEBUG:   c_cflag = 0x0
 > DEBUG:   c_lflag = 0x0 ()
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > ----------------------------------------
 > DEBUG: after init_screen
 > DEBUG: is_a_terminal  = 1
 > DEBUG: smart_terminal = 1
 > DEBUG: interactive    = 1 (Yes)
 > DEBUG: displays       = -1
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > ----------------------------------------
 > DEBUG: within quit(), before end_screen
 > DEBUG: is_a_terminal  = 0
 > DEBUG: smart_terminal = 1
 > DEBUG: interactive    = 1 (Yes)
 > DEBUG: displays       = -1
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > ----------------------------------------
 > DEBUG: within quit(), after end_screen
 > DEBUG: is_a_terminal  = 0
 > DEBUG: smart_terminal = 1
 > DEBUG: interactive    = 1 (Yes)
 > DEBUG: displays       = -1
 > DEBUG: old_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x5cb (ECHOKE,ECHOE,ECHO,ECHOCTL,ISIG,ICANON,IEXTEN,)
 > DEBUG: new_settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > DEBUG: current terminal settings:
 > DEBUG:   c_iflag = 0x2b02
 > DEBUG:   c_oflag = 0x3
 > DEBUG:   c_cflag = 0x4b00
 > DEBUG:   c_lflag = 0x4c3 (ECHOKE,ECHOE,ECHOCTL,ISIG,IEXTEN,)
 > ----------------------------------------
 > 
 > Based on this, you can see that the tcsetattr() call to restore the
 > terminal from old_settings isn't happening.  That call is done in
 > end_screen().
 > 
 > The conditional for that to happen or not is based on the is_a_terminal
 > variable.  And as you can see, is_a_terminal is 0 when end_screen()
 > is called.
 > 
 > You can see that is_a_terminal == 1 after init_screen() is called,
 > but within quit() (but before end_screen()) somehow is_a_terminal
 > is getting reset to 0.  This is what causes the problem.
 > 
 > If you want the patch to see the debug info, let me know.
 > 
 > -- 
 > | Jeremy Chadwick                                 jdc@parodius.com |
 > | Parodius Networking                     http://www.parodius.com/ |
 > | UNIX Systems Administrator                 Mountain View, CA, US |
 > | Making life hard for others since 1977.             PGP 4BD6C0CB |
 > 
 > On Tue, Apr 17, 2012 at 08:28:36AM -0700, Jeremy Chadwick wrote:
 > > (08:22:23 jdc@omake) ~ $ egrep -r -n '(ICANON|ECHO)' /usr/src/contrib/top
 > > /usr/src/contrib/top/screen.c:250:      new_settings.sg_flags &= ~(ECHO|XTABS);
 > > /usr/src/contrib/top/screen.c:278:      /* turn off ICANON, character echo and tab expansion */
 > > /usr/src/contrib/top/screen.c:279:      new_settings.c_lflag &= ~(ICANON|ECHO);
 > > /usr/src/contrib/top/screen.c:302:      /* turn off ICANON, character echo and tab expansion */
 > > /usr/src/contrib/top/screen.c:303:      new_settings.c_lflag &= ~(ICANON|ECHO);
 > > 
 > > So it looks to me like these capabilities aren't being restored
 > > prior to top exiting.  It used to be this way with just "-b", but
 > > now affects "-a" as well.
 > > 
 > > All of those lines are part of init_screen().  However this routine
 > > is filled with #ifdefs.
 > > 
 > > end_screen() has some generic attempt to reset these capabilities
 > > (variable is called old_settings.
 > > 
 > > Possibly the issue is with some kind of code logic bug pertaining to
 > > is_a_terminal or smart_terminal.
 > > 
 > > -- 
 > > | Jeremy Chadwick                                 jdc@parodius.com |
 > > | Parodius Networking                     http://www.parodius.com/ |
 > > | UNIX Systems Administrator                 Mountain View, CA, US |
 > > | Making life hard for others since 1977.             PGP 4BD6C0CB |
 > > 
 > > On Tue, Apr 17, 2012 at 08:19:29AM -0700, Jeremy Chadwick wrote:
 > > > On Tue, Apr 17, 2012 at 12:36:38PM +0300, Konstantin Belousov wrote:
 > > > > On Mon, Apr 16, 2012 at 02:48:33PM -0700, Jeremy Chadwick wrote:
 > > > > > Kostik,
 > > > > > 
 > > > > > Please see the below PR.  I believe you've introduced a problem that
 > > > > > used to just affect the -b flag, but now affects -a as well.
 > > > > > 
 > > > > > Maybe you can fix both.  :-)
 > > > > I cannot reproduce the problem.
 > > > 
 > > > On what, RELENG_9 or what?  This is easily reproducible on every
 > > > RELENG_8 system we have, as well as my home workstation, and a RELENG_8
 > > > FreeBSD instance run under VMware.
 > > > 
 > > > (08:16:25 jdc@omake) ~ $ stty -a
 > > > speed 9600 baud; 43 rows; 132 columns;
 > > > lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
 > > >         -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo
 > > >         -extproc
 > > > iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk
 > > >         brkint -inpck -ignpar -parmrk
 > > > oflags: opost onlcr -ocrnl tab0 -onocr -onlret
 > > > cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
 > > >         -dtrflow -mdmbuf
 > > > cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
 > > >         eol2 = <undef>; erase = ^?; erase2 = ^H; intr = ^C; kill = ^U;
 > > >         lnext = ^V; min = 1; quit = ^\; reprint = ^R; start = ^Q;
 > > >         status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;
 > > > 
 > > > (08:16:27 jdc@omake) ~ $ top -a
 > > > last pid: 62310;  load averages:  0.00,  0.00,  0.00                                                        up 1+11:03:16  08:16:30
 > > > 32 processes:  1 running, 31 sleeping
 > > > CPU:     % user,     % nice,     % system,     % interrupt,     % idle
 > > > Mem: 168M Active, 333M Inact, 375M Wired, 236K Cache, 389M Buf, 2764M Free
 > > > Swap: 8192M Total, 8192M Free
 > > > 
 > > >   PID USERNAME  THR PRI NICE   SIZE    RES STATE   C   TIME   WCPU COMMAND
 > > >  6482 halbot      1  44    0 90940K 75900K select  0   0:14  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/
 > > >   575 root        1  44    0  6900K  1272K select  0   0:06  0.00% /usr/sbin/powerd
 > > > 19810 root        1  44    0 73092K 43876K select  0   0:04  0.00% /usr/local/bin/spamd -c --min-children=4 --min-spare=4 --max
 > > >   572 root        1  44    0 11788K  2560K select  0   0:02  0.00% /usr/sbin/ntpd -c /conf/ME/ntp.conf -p /var/run/ntpd.pid -f
 > > >   362 _pflogd     1  44    0  8116K  1676K bpf     0   0:01  0.00% pflogd: [running] -s 116 -i pflog0 -f /var/log/pflog (pflogd
 > > > 19811 root        1  44    0 75140K 46620K select  0   0:01  0.00% spamd child (perl)
 > > >   657 root        1  44    0 11228K  2832K kqread  2   0:01  0.00% /usr/local/libexec/postfix/master
 > > > 58324 root        1  44    0 10436K  2344K kqread  2   0:00  0.00% /usr/local/sbin/dovecot -c /conf/ME/mail/dovecot.conf
 > > > 19812 root        1  44    0 73092K 44712K select  1   0:00  0.00% spamd child (perl)
 > > >   672 root        1  44    0  7960K  1600K nanslp  0   0:00  0.00% /usr/sbin/cron -s
 > > >   493 root        1  44    0  6904K  1516K select  0   0:00  0.00% /usr/sbin/syslogd -s
 > > >  5912 bind        7  44    0 28836K 18248K kqread  0   0:00  0.00% /usr/sbin/named -t /var/named -u bind
 > > > 58325 dovecot     1  44    0 10432K  1980K kqread  0   0:00  0.00% dovecot/anvil
 > > > 58326 root        1  44    0 10436K  1960K kqread  1   0:00  0.00% dovecot/log
 > > >   663 postfix     1  44    0 11228K  2952K kqread  1   0:00  0.00% qmgr -l -t fifo -u
 > > > 19813 root        1  44    0 73092K 43876K select  2   0:00  0.00% spamd child (perl)
 > > > 19815 root        1  44    0 73092K 43876K select  3   0:00  0.00% spamd child (perl)
 > > > 19814 root        1  44    0 73092K 43876K select  2   0:00  0.00% spamd child (perl)
 > > > 62304 root        1  45    0 18980K  4048K sbwait  2   0:00  0.00% sshd: jdc [priv] (sshd)
 > > > 62302 root        1  45    0 12676K  2544K kqread  0   0:00  0.00% dovecot/auth -w
 > > > 62300 dovecot     1  44    0 12640K  2480K kqread  3   0:00  0.00% dovecot/auth
 > > >   668 root        1  44    0 16420K  3716K select  1   0:00  0.00% /usr/sbin/sshd
 > > > 62307 jdc         1  44    0 10228K  2868K wait    1   0:00  0.00% -bash (bash)
 > > > 62299 root        1  44    0 10432K  2900K kqread  1   0:00  0.00% dovecot/config
 > > > 62306 jdc         1  44    0 18980K  4132K select  1   0:00  0.00% sshd: jdc@pts/0 (sshd)
 > > > 62309 postfix     1  44    0 11232K  2984K kqread  0   0:00  0.00% pickup -l -t fifo -u
 > > > 62301 root        1  44    0 13124K  2644K kqread  0   0:00  0.00% dovecot/ssl-params
 > > > 62310 jdc         1  44    0  9356K  2084K CPU0    0   0:00  0.00% top -a
 > > >   702 root        1  44    0  9008K  1636K select  1   0:00  0.00% /usr/sbin/inetd -C 0
 > > >   360 root        1  76    0  8116K  1636K sbwait  1   0:00  0.00% pflogd: [priv] (pflogd)
 > > >   721 root        1  76    0  6900K  1268K ttyin   1   0:00  0.00% /usr/libexec/getty Pc ttyv0
 > > >   722 root        1  76    0  6900K  1268K ttyin   0   0:00  0.00% /usr/libexec/getty Pc ttyv1
 > > > 
 > > > 
 > > > 
 > > > (08:16:31 jdc@omake) ~ $ speed 9600 baud; 43 rows; 132 columns;
 > > > lflags: -icanon isig iexten -echo echoe -echok echoke -echonl echoctl
 > > >         -echoprt -altwerase -noflsh -tostop -flusho -pendin -nokerninfo
 > > >         -extproc
 > > > iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -ignbrk
 > > >         brkint -inpck -ignpar -parmrk
 > > > oflags: opost onlcr -ocrnl tab0 -onocr -onlret
 > > > cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
 > > >         -dtrflow -mdmbuf
 > > > cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
 > > >         eol2 = <undef>; erase = ^?; erase2 = ^H; intr = ^C; kill = ^U;
 > > >         lnext = ^V; min = 1; quit = ^\; reprint = ^R; start = ^Q;
 > > >         status = ^T; stop = ^S; susp = ^Z; time = 0; werase = ^W;
 > > > 
 > > > 
 > > > All I did was hit "q" to exit top, then typed "stty -a".  None of my
 > > > input was echo'd back to the terminal, which is why you seed the "speed
 > > > 9600 baud ..." on the same line as my shell.
 > > > 
 > > > Note the -icanon and -echo capabilities shown there, while prior to
 > > > running top, they aren't "-" (meaning icanon and echo are enabled).
 > > > 
 > > > -- 
 > > > | Jeremy Chadwick                                 jdc@parodius.com |
 > > > | Parodius Networking                     http://www.parodius.com/ |
 > > > | UNIX Systems Administrator                 Mountain View, CA, US |
 > > > | Making life hard for others since 1977.             PGP 4BD6C0CB |
 > > > 
 
 ----- End forwarded message -----
Responsible-Changed-From-To: freebsd-bugs->eadler 
Responsible-Changed-By: eadler 
Responsible-Changed-When: Wed Apr 18 03:12:28 UTC 2012 
Responsible-Changed-Why:  
I'll take it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=161739 

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Tue, 17 Apr 2012 23:02:58 -0700

 After much investigation, and much assistance from kib@, the root
 cause was found to be sprintf() clobbering the is_a_terminal
 variable.  The fix was simple: use snprintf().
 
 Discussion thread (where most people could not reproduce it, but
 Carl Johnson could):
 
 http://lists.freebsd.org/pipermail/freebsd-stable/2012-April/067223.html
 
 The simple patch for the problem is below.  I've only tested it on
 RELENG_8:
 
 http://jdc.parodius.com/freebsd/161739/memory_corruption_fix.diff
 
 This should be committed to address the problem described in the PR.
 
 
 While I was in the top code, I also took a stab at cleaning up the
 massive numbers of warnings emit (even without -Wall).  I cleaned
 those up for sure.  Lots of function prototypes missing...
 
 With -Wall it still emits some warnings but I've diminished the ones I'm
 familiar with / understood to be safe.  There are still some which I
 can't fix due to lack of familiarity with them (unsure how to get gcc to
 settle down / unsure how to solve).
 
 As such, these patches should not be applied without full review by
 someone much more senior:
 
 http://jdc.parodius.com/freebsd/161739/warns_contrib_top.diff
 http://jdc.parodius.com/freebsd/161739/warns_usrbin_top.diff
 
 HTH!
 
 -- 
 | Jeremy Chadwick                                 jdc@parodius.com |
 | Parodius Networking                     http://www.parodius.com/ |
 | UNIX Systems Administrator                 Mountain View, CA, US |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/161739: commit references a PR
Date: Wed, 18 Apr 2012 10:23:51 +0000 (UTC)

 Author: kib
 Date: Wed Apr 18 10:23:42 2012
 New Revision: 234416
 URL: http://svn.freebsd.org/changeset/base/234416
 
 Log:
   Fix string buffer overflow when preparing the line of output.
   
   PR:	bin/161739
   Submitted by:	Jeremy Chadwick <freebsd jdc parodius com>
   MFC after:	1 week
 
 Modified:
   head/usr.bin/top/machine.c
 
 Modified: head/usr.bin/top/machine.c
 ==============================================================================
 --- head/usr.bin/top/machine.c	Wed Apr 18 09:42:14 2012	(r234415)
 +++ head/usr.bin/top/machine.c	Wed Apr 18 10:23:42 2012	(r234416)
 @@ -933,7 +933,7 @@ format_next_process(caddr_t handle, char
  		p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt;
  		s_tot = total_inblock + total_oublock + total_majflt;
  
 -		sprintf(fmt, io_Proc_format,
 +		snprintf(fmt, sizeof(fmt), io_Proc_format,
  		    pp->ki_pid,
  		    jid_buf,
  		    namelength, namelength, (*get_userid)(pp->ki_ruid),
 @@ -961,7 +961,7 @@ format_next_process(caddr_t handle, char
  		snprintf(thr_buf, sizeof(thr_buf), "%*d ",
  		    sizeof(thr_buf) - 2, pp->ki_numthreads);
  
 -	sprintf(fmt, proc_fmt,
 +	snprintf(fmt, sizeof(fmt), proc_fmt,
  	    pp->ki_pid,
  	    jid_buf,
  	    namelength, namelength, (*get_userid)(pp->ki_ruid),
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: Kostik Belousov <kostikbel@gmail.com>
To: bug-followup@FreeBSD.org, freebsd@jdc.parodius.com, kib@freebsd.org
Cc:  
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Wed, 18 Apr 2012 13:28:30 +0300

 I committed the sprintf->snprintf as the immediate relief.
 
 But the real issue seems to be caused by the large-column terminals.
 Could it be that your TERMCAP has some large value for the 'co'
 attribute ?
 Otherwise the code is quite accurate to not overflow the fmt.
 
 The patch is good to have to prevent more foot-shooting (think
 defensive programming), but it indeed does not fix the real cause.
Responsible-Changed-From-To: eadler->kib 
Responsible-Changed-By: eadler 
Responsible-Changed-When: Wed Apr 18 12:34:09 UTC 2012 
Responsible-Changed-Why:  
over to kib 

http://www.freebsd.org/cgi/query-pr.cgi?pr=161739 

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: Kostik Belousov <kostikbel@gmail.com>
Cc: bug-followup@FreeBSD.org, kib@freebsd.org
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Wed, 18 Apr 2012 07:00:35 -0700

 On Wed, Apr 18, 2012 at 01:28:30PM +0300, Kostik Belousov wrote:
 > I committed the sprintf->snprintf as the immediate relief.
 > 
 > But the real issue seems to be caused by the large-column terminals.
 > Could it be that your TERMCAP has some large value for the 'co'
 > attribute ?
 > Otherwise the code is quite accurate to not overflow the fmt.
 > 
 > The patch is good to have to prevent more foot-shooting (think
 > defensive programming), but it indeed does not fix the real cause.
 
 My terminal width is 132x43.  My TERM is xterm.  I do not override
 or set TERMCAP in any way, so I'm using whatever is in /etc/termcap.
 
 I tried using a smaller terminal size -- 80x25 -- and the problem still
 occurs. I'm not resizing the existing window either; I'm making a brand
 new SSH connection with an 80x25 terminal.
 
 Possibly the problem is with the screen_width and cmdlengthdelta
 variables, which are being used to used to control the width via the
 %.*s argument given to sprintf()?  Or could the problem be with
 printable()?
 
 Here's some more useful data:
 
 (06:37:49 jdc@omake) ~/usr.bin/top $ stty -a | grep col
 speed 9600 baud; 43 rows; 132 columns;
 
 (gdb) watch is_a_terminal
 Watchpoint 1: is_a_terminal
 (gdb) run -a -b
 Starting program: /home/jdc/usr.bin/top/top -a -b
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 
 Old value = 0 '\0'
 New value = 1 '\001'
 init_screen () at /home/jdc/usr.bin/top/../../contrib/top/screen.c:317
 317             putcap(terminal_init);
 (gdb) c
 Continuing.
 Watchpoint 1: is_a_terminal
 
 Old value = 1 '\001'
 New value = 0 '\0'
 0x00000000a0aa1448 in vsprintf () from /lib/libc.so.7
 (gdb) bt
 #0  0x00000000a0aa1448 in vsprintf () from /lib/libc.so.7
 #1  0x00000000a0aa04c8 in sprintf () from /lib/libc.so.7
 #2  0x0000000000406faf in format_next_process (handle=0x511870 "\030\"", get_userid=0x40bc60 <username>, flags=1)
     at machine.c:965
 #3  0x000000000040ab86 in main (argc=3, argv=0x7fffffffea18) at /home/jdc/usr.bin/top/../../contrib/top/top.c:715
 (gdb) f 2
 #2  0x0000000000406faf in format_next_process (handle=0x511870 "\030\"", get_userid=0x40bc60 <username>, flags=1)
     at machine.c:965
 965             sprintf(fmt, proc_fmt,
 (gdb) x/s proc_fmt
 0x40d368 <_fini+2980>:   "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s %2d%7s %5.2f%% %.*s"
 (gdb) x/s fmt
 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  0   0:33  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/h"
 (gdb) p screen_width
 $1 = 128
 (gdb) p cmdlengthdelta
 $2 = 67
 (gdb) p strlen(fmt)
 $3 = 128
 (gdb) x/s cmdbuf
 0xa0e3c060:      "/usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/hal/ha"
 (gdb) p strlen(cmdbuf)
 $4 = 66
 
 And now, the above but using an 80x25 terminal, where the same problem
 happens:
 
 (06:48:22 jdc@omake) ~/usr.bin/top $ stty -a | grep col
 speed 9600 baud; 25 rows; 80 columns;
 
 (gdb) watch is_a_terminal
 Watchpoint 1: is_a_terminal
 (gdb) run -a -b
 Starting program: /home/jdc/usr.bin/top/top -a -b
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 Watchpoint 1: is_a_terminal
 
 Old value = 0 '\0'
 New value = 1 '\001'
 init_screen () at /home/jdc/usr.bin/top/../../contrib/top/screen.c:317
 317             putcap(terminal_init);
 (gdb) c
 Continuing.
 Watchpoint 1: is_a_terminal
 
 Old value = 1 '\001'
 New value = 0 '\0'
 0x00000000a0aa1448 in vsprintf () from /lib/libc.so.7
 (gdb) bt
 #0  0x00000000a0aa1448 in vsprintf () from /lib/libc.so.7
 #1  0x00000000a0aa04c8 in sprintf () from /lib/libc.so.7
 #2  0x0000000000406faf in format_next_process (handle=0x511870 "\030\"",
     get_userid=0x40bc60 <username>, flags=1) at machine.c:965
 #3  0x000000000040ab86 in main (argc=3, argv=0x7fffffffea18)
     at /home/jdc/usr.bin/top/../../contrib/top/top.c:715
 (gdb) f 2
 #2  0x0000000000406faf in format_next_process (handle=0x511870 "\030\"",
     get_userid=0x40bc60 <username>, flags=1) at machine.c:965
 965             sprintf(fmt, proc_fmt,
 (gdb) x/s proc_fmt
 0x40d368 <_fini+2980>:   "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s %2d%7s %5.2f%% %.*s"
 (gdb) x/s fmt
 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  2   0:33  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl/home/halbot/h"
 (gdb) p screen_width
 $1 = 128
 (gdb) p cmdlengthdelta
 $2 = 67
 (gdb) p strlen(fmt)
 $3 = 128
 (gdb) x/s cmdbuf
 0xa0e3c060:      "/usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/hal/ha"
 (gdb) p strlen(cmdbuf)
 $4 = 66
 
 Seems the numbers are the same, so at least internally to top, the
 terminal width doesn't seem to have much to do with it.
 
 I should note that the "top -a -b" output for that process (PID 6482)
 on-screen appears to chop off the last "h" character shown in the
 fmt buffer.  In other words, fmt contains (sans quotes):
 
 " 6482 halbot      1  44    0 90940K 75912K select  2   0:33  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl/home/halbot/h"
 
 What I see on screen with 132x43:
 
   PID USERNAME  THR PRI NICE   SIZE    RES STATE   C   TIME   WCPU COMMAND
  6482 halbot      1  44    0 90940K 75912K select  0   0:33  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/
 
 What I see on screen with 80x25:
 
   PID USERNAME  THR PRI NICE   SIZE    RES STATE   C   TIME   WCPU COMMAND
  6482 halbot      1  44    0 90940K 75912K select  0   0:33  0.00% /usr/local/b
 
 All I could think of to try next was this, on the 80x25 terminal:
 
 (gdb) watch screen_width
 Watchpoint 1: screen_width
 (gdb) watch is_a_terminal
 Watchpoint 2: is_a_terminal
 (gdb) run -a -b
 Starting program: /home/jdc/usr.bin/top/top -a -b
 Watchpoint 1: screen_width
 Watchpoint 2: is_a_terminal
 Watchpoint 1: screen_width
 Watchpoint 2: is_a_terminal
 Watchpoint 1: screen_width
 Watchpoint 2: is_a_terminal
 Watchpoint 1: screen_width
 Watchpoint 2: is_a_terminal
 Watchpoint 1: screen_width
 Watchpoint 2: is_a_terminal
 Watchpoint 1: screen_width
 
 Old value = 0
 New value = 128
 init_termcap (interactive=0)
     at /home/jdc/usr.bin/top/../../contrib/top/screen.c:113
 113         screen_length = 0;
 (gdb) c
 Continuing.
 Watchpoint 2: is_a_terminal
 
 Old value = 0 '\0'
 New value = 1 '\001'
 init_screen () at /home/jdc/usr.bin/top/../../contrib/top/screen.c:317
 317             putcap(terminal_init);
 (gdb) c
 Continuing.
 Watchpoint 2: is_a_terminal
 
 Old value = 1 '\001'
 New value = 0 '\0'
 0x00000000a0aa1448 in vsprintf () from /lib/libc.so.7
 (gdb) p screen_width
 $1 = 128
 

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: Kostik Belousov <kostikbel@gmail.com>
Cc: bug-followup@FreeBSD.org, kib@freebsd.org
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Wed, 18 Apr 2012 07:34:58 -0700

 I thought some more about this.  I believe the problem is that we do
 have a size calculation mistake in the output formatting string, where
 we're not taking into consideration that the 128th byte of fmt needs to
 be NULL.  snprintf() takes care of that for us, while sprintf() doesn't.
 
 Per sprintf() man page:
 
      These functions return the number of characters printed (not including
      the trailing `\0' used to end output to strings) or a negative value if
      an output error occurs, except for snprintf() and vsnprintf(), which
      return the number of characters that would have been printed if the size
      were unlimited (again, not including the final `\0').
 
 And snprintf():
 
      The snprintf() and vsnprintf() functions will write at most size-1 of the
      characters printed into the output string (the size'th character then
      gets the terminating `\0'); if the return value is greater than or equal
      to the size argument, the string was too short and some of the printed
      characters were discarded.  The output is always null-terminated.
 
 So I modified the code that uses sprintf() to catch the return value
 to find out how many characters it was writing.  Line numbers will be
 different here because I added "int retlen" as a variable.  I'm also
 using "run -a -b -U halbot" since the halbot process (with long argument
 lines) seems to tickle the problem.  My break statement is at the
 free(cmdbuf) line right after the sprintf().
 
 (gdb) break machine.c:982
 Breakpoint 1 at 0x406fb5: file machine.c, line 982.
 (gdb) run -a -b -U halbot
 Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 
 Breakpoint 1, format_next_process (handle=0x511870 "\210\003", get_userid=0x40bc60 <username>, flags=1) at machine.c:982
 982             free(cmdbuf);
 (gdb) x/s fmt
 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  2   0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/h"
 (gdb) p retlen
 $1 = 128
 (gdb) p strlen(fmt)
 $2 = 128
 
 Note the "h" at the end of the fmt string there, AND note what
 strlen(fmt) returns -- it means the NULL is at the 129th byte, which
 happens to be where is_a_terminal resides.
 
 Now let's do the same thing but using snprintf():
 
 (gdb) b machine.c:982
 Breakpoint 1 at 0x406fbb: file machine.c, line 982.
 (gdb) run -a -b -U halbot
 Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 
 Breakpoint 1, format_next_process (handle=0x511870 "\210\003", get_userid=0x40bc60 <username>, flags=1) at machine.c:982
 982             free(cmdbuf);
 (gdb) x/s fmt
 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  0    0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl/home/halbot/"
 (gdb) p retlen
 $1 = 128
 (gdb) p strlen(fmt)
 $2 = 127
 
 I think this is more or less proof that our size calculation is wrong
 (off-by-one), meaning it's trying to fill the entire fmt[128] buffer
 with data, rather than keeping in mind the last byte of fmt should be
 NULL.  snprintf() keeps that from happening, thankfully.
 
 -- 
 | Jeremy Chadwick                                 jdc@parodius.com |
 | Parodius Networking                     http://www.parodius.com/ |
 | UNIX Systems Administrator                 Mountain View, CA, US |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |

From: Konstantin Belousov <kostikbel@gmail.com>
To: Jeremy Chadwick <freebsd@jdc.parodius.com>
Cc: bug-followup@freebsd.org
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO terminal capabilities when exiting
Date: Wed, 18 Apr 2012 17:39:45 +0300

 --zAXQCMFeJt7J/Poe
 Content-Type: text/plain; charset=koi8-r
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable
 
 On Wed, Apr 18, 2012 at 07:34:58AM -0700, Jeremy Chadwick wrote:
 > I thought some more about this.  I believe the problem is that we do
 > have a size calculation mistake in the output formatting string, where
 > we're not taking into consideration that the 128th byte of fmt needs to
 > be NULL.  snprintf() takes care of that for us, while sprintf() doesn't.
 >=20
 > Per sprintf() man page:
 >=20
 >      These functions return the number of characters printed (not includi=
 ng
 >      the trailing `\0' used to end output to strings) or a negative value=
  if
 >      an output error occurs, except for snprintf() and vsnprintf(), which
 >      return the number of characters that would have been printed if the =
 size
 >      were unlimited (again, not including the final `\0').
 >=20
 > And snprintf():
 >=20
 >      The snprintf() and vsnprintf() functions will write at most size-1 o=
 f the
 >      characters printed into the output string (the size'th character then
 >      gets the terminating `\0'); if the return value is greater than or e=
 qual
 >      to the size argument, the string was too short and some of the print=
 ed
 >      characters were discarded.  The output is always null-terminated.
 >=20
 > So I modified the code that uses sprintf() to catch the return value
 > to find out how many characters it was writing.  Line numbers will be
 > different here because I added "int retlen" as a variable.  I'm also
 > using "run -a -b -U halbot" since the halbot process (with long argument
 > lines) seems to tickle the problem.  My break statement is at the
 > free(cmdbuf) line right after the sprintf().
 >=20
 > (gdb) break machine.c:982
 > Breakpoint 1 at 0x406fb5: file machine.c, line 982.
 > (gdb) run -a -b -U halbot
 > Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 >=20
 > Breakpoint 1, format_next_process (handle=3D0x511870 "\210\003=E1=A0", ge=
 t_userid=3D0x40bc60 <username>, flags=3D1) at machine.c:982
 > 982             free(cmdbuf);
 > (gdb) x/s fmt
 > 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  2   =
 0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/h"
 > (gdb) p retlen
 > $1 =3D 128
 > (gdb) p strlen(fmt)
 > $2 =3D 128
 >=20
 > Note the "h" at the end of the fmt string there, AND note what
 > strlen(fmt) returns -- it means the NULL is at the 129th byte, which
 > happens to be where is_a_terminal resides.
 >=20
 > Now let's do the same thing but using snprintf():
 >=20
 > (gdb) b machine.c:982
 > Breakpoint 1 at 0x406fbb: file machine.c, line 982.
 > (gdb) run -a -b -U halbot
 > Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 >=20
 > Breakpoint 1, format_next_process (handle=3D0x511870 "\210\003=E1=A0", ge=
 t_userid=3D0x40bc60 <username>, flags=3D1) at machine.c:982
 > 982             free(cmdbuf);
 > (gdb) x/s fmt
 > 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  0   =
  0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl/home/halbot/"
 > (gdb) p retlen
 > $1 =3D 128
 > (gdb) p strlen(fmt)
 > $2 =3D 127
 >=20
 > I think this is more or less proof that our size calculation is wrong
 > (off-by-one), meaning it's trying to fill the entire fmt[128] buffer
 > with data, rather than keeping in mind the last byte of fmt should be
 > NULL.  snprintf() keeps that from happening, thankfully.
 
 Great, thank you for the good analysis.
 Would you make the patch for this ?
 
 I still think that snprintf() should be kept as is to prevent future
 issues.
 
 --zAXQCMFeJt7J/Poe
 Content-Type: application/pgp-signature
 Content-Disposition: inline
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (FreeBSD)
 
 iEYEARECAAYFAk+O0jEACgkQC3+MBN1Mb4j/tQCdHVYqGhKMhYsLVwxV8LYJiRlu
 ry0AoOrenCRRYAlUbbWFnsVE/7TKk3Ru
 =eLrR
 -----END PGP SIGNATURE-----
 
 --zAXQCMFeJt7J/Poe--

From: Jeremy Chadwick <freebsd@jdc.parodius.com>
To: Konstantin Belousov <kostikbel@gmail.com>
Cc: bug-followup@freebsd.org
Subject: Re: bin/161739: top(1): top -b does not restore ICANON and ECHO
 terminal capabilities when exiting
Date: Wed, 18 Apr 2012 07:59:52 -0700

 On Wed, Apr 18, 2012 at 05:39:45PM +0300, Konstantin Belousov wrote:
 > On Wed, Apr 18, 2012 at 07:34:58AM -0700, Jeremy Chadwick wrote:
 > > I thought some more about this.  I believe the problem is that we do
 > > have a size calculation mistake in the output formatting string, where
 > > we're not taking into consideration that the 128th byte of fmt needs to
 > > be NULL.  snprintf() takes care of that for us, while sprintf() doesn't.
 > > 
 > > Per sprintf() man page:
 > > 
 > >      These functions return the number of characters printed (not including
 > >      the trailing `\0' used to end output to strings) or a negative value if
 > >      an output error occurs, except for snprintf() and vsnprintf(), which
 > >      return the number of characters that would have been printed if the size
 > >      were unlimited (again, not including the final `\0').
 > > 
 > > And snprintf():
 > > 
 > >      The snprintf() and vsnprintf() functions will write at most size-1 of the
 > >      characters printed into the output string (the size'th character then
 > >      gets the terminating `\0'); if the return value is greater than or equal
 > >      to the size argument, the string was too short and some of the printed
 > >      characters were discarded.  The output is always null-terminated.
 > > 
 > > So I modified the code that uses sprintf() to catch the return value
 > > to find out how many characters it was writing.  Line numbers will be
 > > different here because I added "int retlen" as a variable.  I'm also
 > > using "run -a -b -U halbot" since the halbot process (with long argument
 > > lines) seems to tickle the problem.  My break statement is at the
 > > free(cmdbuf) line right after the sprintf().
 > > 
 > > (gdb) break machine.c:982
 > > Breakpoint 1 at 0x406fb5: file machine.c, line 982.
 > > (gdb) run -a -b -U halbot
 > > Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 > > 
 > > Breakpoint 1, format_next_process (handle=0x511870 "\210\003??", get_userid=0x40bc60 <username>, flags=1) at machine.c:982
 > > 982             free(cmdbuf);
 > > (gdb) x/s fmt
 > > 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  2   0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl /home/halbot/h"
 > > (gdb) p retlen
 > > $1 = 128
 > > (gdb) p strlen(fmt)
 > > $2 = 128
 > > 
 > > Note the "h" at the end of the fmt string there, AND note what
 > > strlen(fmt) returns -- it means the NULL is at the 129th byte, which
 > > happens to be where is_a_terminal resides.
 > > 
 > > Now let's do the same thing but using snprintf():
 > > 
 > > (gdb) b machine.c:982
 > > Breakpoint 1 at 0x406fbb: file machine.c, line 982.
 > > (gdb) run -a -b -U halbot
 > > Starting program: /home/jdc/usr.bin/top/top -a -b -U halbot
 > > 
 > > Breakpoint 1, format_next_process (handle=0x511870 "\210\003??", get_userid=0x40bc60 <username>, flags=1) at machine.c:982
 > > 982             free(cmdbuf);
 > > (gdb) x/s fmt
 > > 0x511880 <fmt>:  " 6482 halbot      1  44    0 90940K 75912K select  0    0:34  0.00% /usr/local/bin/perl /home/halbot/hal/halbot.pl/home/halbot/"
 > > (gdb) p retlen
 > > $1 = 128
 > > (gdb) p strlen(fmt)
 > > $2 = 127
 > > 
 > > I think this is more or less proof that our size calculation is wrong
 > > (off-by-one), meaning it's trying to fill the entire fmt[128] buffer
 > > with data, rather than keeping in mind the last byte of fmt should be
 > > NULL.  snprintf() keeps that from happening, thankfully.
 > 
 > Great, thank you for the good analysis.
 > Would you make the patch for this ?
 > 
 > I still think that snprintf() should be kept as is to prevent future
 > issues.
 
 You're welcome, and thank you for the commit!  I fully agree snprintf()
 should be kept; as you said, it's good/safe/defencive programming.
 
 I'll see if I can figure out where the size calculation error is.  I'm
 thinking it's with the length specifier for %.*s used at the end of
 proc_fmt, thus I'm thinking this would probably be the right fix:
 
   screen_width > cmdlengthdelta ? screen_width-cmdlengthdelta-1 : 0,
 
 I'm basing that on the below, but I don't want to be hasty.
 
 (gdb) x/s proc_fmt
 0x40d368 <_fini+2980>:   "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s %2d%7s > %5.2f%% %.*s"
 (gdb) p screen_width
 $1 = 128
 (gdb) p cmdlengthdelta
 $2 = 67
 (gdb) p strlen(cmdbuf)
 $4 = 66
 
  965         sprintf(fmt, proc_fmt,
  966             pp->ki_pid,
  967             jid_buf,
  968             namelength, namelength, (*get_userid)(pp->ki_ruid),
  969             thr_buf,
  970             pp->ki_pri.pri_level - PZERO,
  971             format_nice(pp),
  972             format_k2(PROCSIZE(pp)),
  973             format_k2(pagetok(pp->ki_rssize)),
  974             status,
  975             smpmode ? pp->ki_lastcpu : 0,
  976             format_time(cputime),
  977             ps.wcpu ? 100.0 * weighted_cpu(pct, pp) : 100.0 * pct,
  978             screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0,
  979             printable(cmdbuf));
 
 I may need to figure out when this bug was introduced (maybe when adding
 the -a flag many years ago) and go from there.  The problem only
 seems to happen when -a is used and a process has a very long argument
 list, which is why (I think) only some people see the problem.
 
 -- 
 | Jeremy Chadwick                                 jdc@parodius.com |
 | Parodius Networking                     http://www.parodius.com/ |
 | UNIX Systems Administrator                 Mountain View, CA, US |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/161739: commit references a PR
Date: Wed, 25 Apr 2012 04:53:19 +0000 (UTC)

 Author: kib
 Date: Wed Apr 25 04:53:04 2012
 New Revision: 234667
 URL: http://svn.freebsd.org/changeset/base/234667
 
 Log:
   MFC r234416:
   Fix string buffer overflow when preparing the line of output.
   
   PR:	bin/161739
 
 Modified:
   stable/9/usr.bin/top/machine.c
 Directory Properties:
   stable/9/usr.bin/top/   (props changed)
 
 Modified: stable/9/usr.bin/top/machine.c
 ==============================================================================
 --- stable/9/usr.bin/top/machine.c	Wed Apr 25 02:46:13 2012	(r234666)
 +++ stable/9/usr.bin/top/machine.c	Wed Apr 25 04:53:04 2012	(r234667)
 @@ -933,7 +933,7 @@ format_next_process(caddr_t handle, char
  		p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt;
  		s_tot = total_inblock + total_oublock + total_majflt;
  
 -		sprintf(fmt, io_Proc_format,
 +		snprintf(fmt, sizeof(fmt), io_Proc_format,
  		    pp->ki_pid,
  		    jid_buf,
  		    namelength, namelength, (*get_userid)(pp->ki_ruid),
 @@ -961,7 +961,7 @@ format_next_process(caddr_t handle, char
  		snprintf(thr_buf, sizeof(thr_buf), "%*d ",
  		    sizeof(thr_buf) - 2, pp->ki_numthreads);
  
 -	sprintf(fmt, proc_fmt,
 +	snprintf(fmt, sizeof(fmt), proc_fmt,
  	    pp->ki_pid,
  	    jid_buf,
  	    namelength, namelength, (*get_userid)(pp->ki_ruid),
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: bin/161739: commit references a PR
Date: Wed, 25 Apr 2012 04:57:39 +0000 (UTC)

 Author: kib
 Date: Wed Apr 25 04:57:29 2012
 New Revision: 234668
 URL: http://svn.freebsd.org/changeset/base/234668
 
 Log:
   MFC r234416:
   Fix string buffer overflow when preparing the line of output.
   
   PR:	bin/161739
 
 Modified:
   stable/8/usr.bin/top/machine.c
 Directory Properties:
   stable/8/usr.bin/top/   (props changed)
 
 Modified: stable/8/usr.bin/top/machine.c
 ==============================================================================
 --- stable/8/usr.bin/top/machine.c	Wed Apr 25 04:53:04 2012	(r234667)
 +++ stable/8/usr.bin/top/machine.c	Wed Apr 25 04:57:29 2012	(r234668)
 @@ -932,7 +932,7 @@ format_next_process(caddr_t handle, char
  		p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt;
  		s_tot = total_inblock + total_oublock + total_majflt;
  
 -		sprintf(fmt, io_Proc_format,
 +		snprintf(fmt, sizeof(fmt), io_Proc_format,
  		    pp->ki_pid,
  		    jid_buf,
  		    namelength, namelength, (*get_userid)(pp->ki_ruid),
 @@ -960,7 +960,7 @@ format_next_process(caddr_t handle, char
  		snprintf(thr_buf, sizeof(thr_buf), "%*d ",
  		    sizeof(thr_buf) - 2, pp->ki_numthreads);
  
 -	sprintf(fmt, proc_fmt,
 +	snprintf(fmt, sizeof(fmt), proc_fmt,
  	    pp->ki_pid,
  	    jid_buf,
  	    namelength, namelength, (*get_userid)(pp->ki_ruid),
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: kib 
State-Changed-When: Wed Apr 25 05:23:54 UTC 2012 
State-Changed-Why:  
Merged into all supported branches. 
Cleanups/code tidying should be handled by somebody else. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=161739 
>Unformatted:
