zsh-workers
 help / color / mirror / code / Atom feed
* Obscure zsh history overflow with segfault
@ 2012-01-20 19:42 Christian Neukirchen
  2012-01-21  2:39 ` Daniel Shahaf
  0 siblings, 1 reply; 5+ messages in thread
From: Christian Neukirchen @ 2012-01-20 19:42 UTC (permalink / raw)
  To: zsh-workers

Hi,

I just found this:

% zsh --version    
zsh 4.3.15 (x86_64-unknown-linux-gnu)
% gdb --args zsh -f
(gdb) r
Starting program: /bin/zsh -f
juno% asdfgh
zsh: command not found: asdfgh
juno% r 100 asdfgh
[... lots of repeat]
r 100 asdfgh
asdfgh
r 100 asdfgh
asdfgh
r 100 asdfgh
asdfgh
r 100 asdfgh
asdfgh
r 100 asdfgh
asdfgh
r 100 asdfgh
asdfgh
r 100 asdfgh
[... lots of repeat]
Program received signal SIGSEGV, Segmentation fault.
0x0000000000450792 in zhalloc ()
#0  0x0000000000450792 in zhalloc ()
#1  0x0000000000451b76 in hcalloc ()
#2  0x0000000000449bc7 in ?? ()
#3  0x000000000044aa54 in zshlex ()
#4  0x0000000000465ca7 in ?? ()
#5  0x000000000046685d in ?? ()
#6  0x0000000000466a9c in ?? ()
#7  0x0000000000466b3b in ?? ()
#8  0x0000000000467768 in ?? ()
#9  0x000000000046781b in ?? ()
#10 0x000000000046781b in ?? ()
#11 0x000000000046781b in ?? ()
#12 0x000000000046781b in ?? ()
#13 0x000000000046781b in ?? ()
#14 0x000000000046781b in ?? ()
#15 0x000000000046781b in ?? ()
[... lots of exactly the same function]
#173682 0x000000000046781b in ?? ()
#173683 0x000000000046781b in ?? ()
#173684 0x000000000046781b in ?? ()
#173685 0x00000000004678e1 in parse_event ()
#173686 0x000000000043d3e9 in loop ()
#173687 0x000000000041c4a2 in bin_fc ()
#173688 0x000000000041cc64 in execbuiltin ()
#173689 0x000000000042ac62 in ?? ()
#173690 0x000000000042b2be in ?? ()
#173691 0x000000000042b675 in ?? ()
#173692 0x000000000042c73d in execlist ()
#173693 0x000000000042cccf in execode ()
#173694 0x000000000043d56f in loop ()
#173695 0x000000000041c4a2 in bin_fc ()
#173696 0x000000000041cc64 in execbuiltin ()
[... lots of repeat]
#173820 0x000000000042c73d in execlist ()
#173821 0x000000000042cccf in execode ()
#173822 0x000000000043d56f in loop ()
#173823 0x000000000041c4a2 in bin_fc ()
#173824 0x000000000041cc64 in execbuiltin ()
#173825 0x000000000042ac62 in ?? ()
#173826 0x000000000042b2be in ?? ()
#173827 0x000000000042b675 in ?? ()
#173828 0x000000000042c73d in execlist ()
#173829 0x000000000042cccf in execode ()
#173830 0x000000000043d442 in loop ()
#173831 0x00000000004401de in zsh_main ()
#173832 0x00007ffff710138d in __libc_start_main () from /lib/libc.so.6
#173833 0x000000000040ec11 in _start ()

Any ideas?  TBH, I don't even know what "r 100 asdfgh" should do,
it was a mistyping of "repeat 100 asdfgh".

Cheers,
-- 
Christian Neukirchen  <chneukirchen@gmail.com>  http://chneukirchen.org


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Obscure zsh history overflow with segfault
  2012-01-20 19:42 Obscure zsh history overflow with segfault Christian Neukirchen
@ 2012-01-21  2:39 ` Daniel Shahaf
  2012-01-24 20:28   ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: Daniel Shahaf @ 2012-01-21  2:39 UTC (permalink / raw)
  To: Christian Neukirchen; +Cc: zsh-workers

Christian Neukirchen wrote on Fri, Jan 20, 2012 at 20:42:10 +0100:
> Any ideas?  TBH, I don't even know what "r 100 asdfgh" should do,
> it was a mistyping of "repeat 100 asdfgh".

% which r
r: shell built-in command
% man zshall | grep -w r | grep -w fc 
       r      Same as fc -e -.
% run-help fc | head -30
...
% 

Apparently, 'r 100 foo' should re-execute the portion of your history
from event #100 to event 'foo'.

Sounds like a bad idea...


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Obscure zsh history overflow with segfault
  2012-01-21  2:39 ` Daniel Shahaf
@ 2012-01-24 20:28   ` Bart Schaefer
  2012-01-26 20:57     ` Peter Stephenson
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Schaefer @ 2012-01-24 20:28 UTC (permalink / raw)
  To: zsh-workers

On Jan 21,  4:39am, Daniel Shahaf wrote:
}
} Apparently, 'r 100 foo' should re-execute the portion of your history
} from event #100 to event 'foo'.
} 
} Sounds like a bad idea...

Interesting.  Here we are:

1516                /*
1517                 * Nasty behaviour results if we use the current history
1518                 * line here.  Treat it as if it doesn't exist, unless
1519                 * that gives us an empty range.
1520                 */
1521                if (last >= curhist) {
1522                    last = curhist - 1;
1523                    if (first > last) {
1524                        unqueue_signals();
1525                        zwarnnam("fc",
1526                          "current history line would recurse endlessly, aborted");
1527                        fclose(out);
1528                        unlink(fil);
1529                        return 1;
1530                    }
1531                }

In the situation in this bug, first > last is true but last >= curhist
is false.  I believe that means that even though this is an infinite
loop, we don't detect that it will be.

However, it's worse than that:  If HIST_NO_STORE is not set, then even
when last < curhist and first <= last, its possible for the history to
contain an "r" command, which will be executed, retrieving a history
that contains an "r" command, which ...

One possible approach is for bin_fc to refuse to recursively run the
"r" command (same for "fc -e -").  However, it's then still possible
for the user to shoot himself with "fc -e cat ..." (or any other no-op
editor command); so short of disallowing any sort of recursive entry
into "fc" when -l is not given, this probably has to be treated as a
user error.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Obscure zsh history overflow with segfault
  2012-01-24 20:28   ` Bart Schaefer
@ 2012-01-26 20:57     ` Peter Stephenson
  2012-01-27  4:59       ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: Peter Stephenson @ 2012-01-26 20:57 UTC (permalink / raw)
  To: zsh-workers

On Tue, 24 Jan 2012 12:28:19 -0800
Bart Schaefer <schaefer@brasslantern.com> wrote:

> On Jan 21,  4:39am, Daniel Shahaf wrote:
> }
> } Apparently, 'r 100 foo' should re-execute the portion of your history
> } from event #100 to event 'foo'.
> } 
> } Sounds like a bad idea...
> 
> Interesting.  Here we are:
> 
> 1516                /*
> 1517                 * Nasty behaviour results if we use the current history
> 1518                 * line here.  Treat it as if it doesn't exist, unless
> 1519                 * that gives us an empty range.
> 1520                 */
> 1521                if (last >= curhist) {
> 1522                    last = curhist - 1;
> 1523                    if (first > last) {
> 1524                        unqueue_signals();
> 1525                        zwarnnam("fc",
> 1526                          "current history line would recurse endlessly, aborted");
> 1527                        fclose(out);
> 1528                        unlink(fil);
> 1529                        return 1;
> 1530                    }
> 1531                }
> 
> In the situation in this bug, first > last is true but last >= curhist
> is false.  I believe that means that even though this is an infinite
> loop, we don't detect that it will be.

I'm not really following the problem, but do you mean something like
this?

Index: Src/builtin.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/builtin.c,v
retrieving revision 1.254
diff -p -u -r1.254 builtin.c
--- Src/builtin.c	29 Aug 2011 17:21:40 -0000	1.254
+++ Src/builtin.c	26 Jan 2012 20:56:28 -0000
@@ -1652,6 +1652,12 @@ fclist(FILE *f, Options ops, zlong first
 	last = first;
 	first = tmp;
     }
+    if (first > last) {
+	zwarnnam("fc", "history events are in wrong order, aborted");
+	if (f != stdout)
+	    fclose(f);
+	return 1;
+    }
     /* suppress "no substitution" warning if no substitution is requested */
     if (!subs)
 	fclistdone = 1;

-- 
Peter Stephenson <p.w.stephenson@ntlworld.com>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Obscure zsh history overflow with segfault
  2012-01-26 20:57     ` Peter Stephenson
@ 2012-01-27  4:59       ` Bart Schaefer
  0 siblings, 0 replies; 5+ messages in thread
From: Bart Schaefer @ 2012-01-27  4:59 UTC (permalink / raw)
  To: zsh-workers

On Jan 26,  8:57pm, Peter Stephenson wrote:
} Subject: Re: Obscure zsh history overflow with segfault
}
} On Tue, 24 Jan 2012 12:28:19 -0800
} 
} > 1521                if (last >= curhist) {
} > 1522                    last = curhist - 1;
} > 1523                    if (first > last) {
} > 1529                        return 1;
} > 
} > In the situation in this bug, first > last is true but last >= curhist
} > is false.  I believe that means that even though this is an infinite
} > loop, we don't detect that it will be.
} 
} I'm not really following the problem, but do you mean something like
} this?
} 
} +    if (first > last) {
} +	zwarnnam("fc", "history events are in wrong order, aborted");
} +	if (f != stdout)
} +	    fclose(f);
} +	return 1;
} +    }

That would catch some nonsense inputs, but it doesn't prevent infinite
recursion on other nonsense, so the question is whether to bother with
catching nonsense at all, or just toss up our hands and say "garbage
in, garbage out."

Here's the easiest way to reproduce from "zsh -f":

% echo Start here
Start here
% r 1 echo
Start here
% r 2

At this point "r 2" loads the command "r 1 echo" and executes it.  This
in turn runs all the commands from event 1 to event 3, which happens to
include event 2, which is "r 1 echo" again, and we loop.  Even if the
original event 2 falls off the top due to HISTSIZE, there is always
another "echo" to be found, and always another "r 1 echo" somewhere
between the mininum history number and the "echo", so we never run out
of "r 1 echo" commands until we run out of stack space to keep calling
loop() recursively out of bin_fc().

The case of "r 100 foo" is the same thing except that the "too large"
number 100 is treated as curhist.

Incidentally, in zsh 4.2.x it was possible to stop this recursion with
a tty interrupt, but in 4.3.13 the interrupt is ignored and the loop
runs until stack overflow causes a segfault.  There are definitely some
signal handling "improvements" that have caused edge case regression in
recent revisions.

My other point was that one way to prevent the infinite recursion is to
prevent *any* recursion:

--- Src/builtin.c       20 Dec 2011 17:13:38 -0000      1.52
+++ Src/builtin.c       24 Jan 2012 20:54:08 -0000
@@ -1532,6 +1532,7 @@
            ops->ind['n'] = 1;  /* No line numbers here. */
            if (!fclist(out, ops, first, last, asgf, pprog)) {
                char *editor;
+               static char *loop_editor = 0;
 
                if (func == BIN_R)
                    editor = "-";
@@ -1545,12 +1546,19 @@
                    editor = DEFAULT_FCEDIT;
 
                unqueue_signals();
-               if (fcedit(editor, fil)) {
+               if (loop_editor) {
+                 zwarnnam("fc",
+                          "-e %s would recurse, aborted",
+                          loop_editor);
+                 retval = 1;
+               } else if (fcedit(editor, fil)) {
                    if (stuff(fil))
                        zwarnnam("fc", "%e: %s", errno, s);
                    else {
+                       loop_editor = editor;
                        loop(0,1);
                        retval = lastval;
+                       loop_editor = 0;
                    }
                }
            } else

However, that may be too draconian.  A static counter could be used
to allow some limited recursion depth (as for recursive functions),
but again it may be better just to say PEBKAC.


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-01-27  5:00 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-20 19:42 Obscure zsh history overflow with segfault Christian Neukirchen
2012-01-21  2:39 ` Daniel Shahaf
2012-01-24 20:28   ` Bart Schaefer
2012-01-26 20:57     ` Peter Stephenson
2012-01-27  4:59       ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).