Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 19 Apr 2000 16:20:02 -0700 (PDT)
From:      Anatoly Vorobey <mellon@pobox.com>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: bin/3170: vi freaks and dump core if user doesn't exist
Message-ID:  <200004192320.QAA64259@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/3170; it has been noted by GNATS.

From: Anatoly Vorobey <mellon@pobox.com>
To: Bill Fenner <fenner@research.att.com>
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: bin/3170: vi freaks and dump core if user doesn't exist
Date: Thu, 20 Apr 2000 02:13:26 +0000

 I've tracked down the bug to a fundamental conflict between vi's
 refresh engine and its warning display engine. The reason "deleted user"
 is at all relevant for reproducing the bug is that vi in that case tries
 to display two warnings at once (about nonexistent user and about
 impossibility of recovering the file in case of failure). In such a case,
 vi will want to prompt user with "Press a key to continue" and until
 it does, it won't refresh the screen; but if this whole condition was
 caused by a multiple-chars key (such as Up, mapped to Esc-something),
 then it also won't display the prompt until the rest of the chars in
 the buffer are processed, and to do that it needs to refresh the screen.
 Hence, a deadlock. The bug is there in all nvi versions including the
 latest one.
 
 That was a tricky bastard. I've no idea how to fix it though, the refresh
 code is a living nightmare and several of straightforward attempts were
 to no avail. I attach for the reference a message I sent to the maintainer
 a few days ago.
 
 Date: Mon, 17 Apr 2000 01:08:45 +0000
 From: Anatoly Vorobey <mellon@pobox.com>
 To: bostic@bostic.com
 Subject: an nvi bug
 
 Dear Keith,
 
 I've been trying to find the bug which apparently has been in nvi
 for quite some time, and is currently manifestable e.g. in all
 versions of FreeBSD (I work with -CURRENT). Although FreeBSD uses
 an old version, I just verified that the bug is still there in 1.79.
 
 There's some description of how to repeat the bug at
 http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/3170 ; and see also
 below for more exact reproduction which never fails.
 
 Reproduction:
     Create a user, login as the user, delete the user with vipw(1)
     while the user stays logged in. Now the user goes into vi and
     tries to edit a small file with several rows (I used his default
     ~/.login_conf, for example). Immediately after invoking vi with
     the filename at command line, the user presses 'i', then preses
     Enter until all but the first line of the file have scrolled 
     down and the first line is on the last screen row, and at that
     moment he presses the Up key. vi tried to display the warning
     about nonexistent user and impossibility of recovering the file,
     freaks, hangs and eventually dumps core.
 
     On the other hand, if instead of the Up key you simply press Esc,
     vi successfully displays the warning, lets you press a key to 
     continue and goes on fine.
 
 I think I've found what the reason is, but I don't know the best way
 to fix the bug, hence this letter.
 
 The fact that the user has been deleted is only relevant insomuch as
 vi tries to present two-line warning (two separate warnings in fact)
 with calls to msgq(). The warnings come from rcv_init() and rcv_mailfile(),
 and rcv_init() is called from bd_set() which is the first function
 to be called by txt_resolve() in order to finally assimilate the inputed
 text into the file (it's followed by a bunch of bd_append()'s which are
 irrelevant).
 
 Now when the user pressed the Up key, the first character to register
 was Escape, with more mapped ones on the way. Escape triggered 
 txt_resolve() and eventual exit from the 'insert' function in v_itxt.c
 which was working all along till now since we started by Inserting.
 As the control goes one more time through the main loop of vi.c, we
 see:
 
                 /* Resolve messages. */ 
                 if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0)) 
                         goto ret; 
                  
                 /* 
                  * If not skipping a refresh, return to command mode and 
                  * refresh the screen. 
                  */ 
                 if (F_ISSET(vip, VIP_S_REFRESH)) 
                         F_CLR(vip, VIP_S_REFRESH); 
                 else  { 
                         sp->showmode = SM_COMMAND; 
                         if (vs_refresh(sp, 0)) 
                                 goto ret; 
                 } 
 
 Now when the user was pressing Escape, the !MAPPED_KEYS_WAITING 
 condition succeeded and vs_resolve() was called, which called
 vs_scroll(), and the latter displayed the "press any key" line
 and after pressing a key cleared up the two lines of scrolled
 warnings that were still there.
 
 But with a mapped key such as Up, the condition fails, and the control
 goes straight to vs_refresh(). Maybe vs_refresh() should be denied
 running until all remaining chars keys have been processed? I am not
 sure. Anyway, vs_refresh() goes to vs_paint() and only asks it to
 UPDATE_CURSOR. vs_paint() eventually needs to call vs_line() to find
 out where the cursor is. But in the beginning of vs_line():
 
         /*                                         
          * If ex modifies the screen after ex output is already on the 
 	 * screen\
          * don't touch it -- we'll get scrolling wrong, at best. 
          */ 
         if (!F_ISSET(sp, SC_TINPUT_INFO) && VIP(sp)->totalcount > 1) 
                 return (0); 
         if (F_ISSET(sp, SC_SCR_EXWROTE) && smp - HMAP != LASTLINE(sp)) 
                 return (0);                       
 
 
 The first test succeeds, since totalcount *is* >1 as we have two
 warnings pending on the screen -- and vs_line() immediately returns.
 vs_paint() freaks and calls itself recursively with the same flags,
 and we have an infinite recursion which gives way to the dump core when
 the stack is exhausted.
 
 That's it. In the case of Escape key, vs_resolve() had been called
 earlier and cleared totalcount, so the test fails and vs_line()
 dutifully does its work. However, one obviously can't introduce a call to
 vs_resolve() before the mapped characters have been processed.
 
 So I don't know exactly how to handle this best; the source of vs_paint()
 and friends is too complicated for me. Hopefully you'll know what to
 do with it, and please drop a line if you do so that I could submit
 a patch to the FreeBSD tree as well.
 
 Thanks,
 and all the best,
 Anatoly.
 
 -- 
 Anatoly Vorobey,
 mellon@pobox.com http://pobox.com/~mellon/
 "Angels can fly because they take themselves lightly" - G.K.Chesterton
 
 
 
 -- 
 Anatoly Vorobey,
 mellon@pobox.com http://pobox.com/~mellon/
 "Angels can fly because they take themselves lightly" - G.K.Chesterton
 


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200004192320.QAA64259>