Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [BUG] Zsh crashes when using autocomplete because of memory unsafety (double free)



Petr Šťastný wrote on Fri, 26 Jun 2020 17:03 +0200:
> Note: I'm using ohmyzsh to provide autocomplete and I can't reproduce it
> without ohmyzsh, but since ohmyzsh is written entirely in shell, this
> should be an issue in zsh itself.
>
> When I trigger autocomplete in one position, zsh crashes, yielding one
> of the following messages, seemingly at random:
>
> (none, zsh crashes silently)         x12
> double free or corruption (out)     x5
>
> Zsh crashed each time I tried it.
>
> zsh 5.8 (x86_64-pc-linux-gnu)
> ohmyzsh, commit 6152ac30bede172ba0422a8610dc796948ae1546
>
> Minimal setup:
> $ alias a='""'
>            ^
>
> Place cursor so it stands on the first " character and press Tab to
> trigger autocomplete. Zsh crashes.
>
> I have almost default ohmyzsh installation, the only thing I changed is
> theme (to `mrtazz`) and `git` plugin, none of which should affect this.
> Zsh config itself (after being modified by ohmyzsh) is unchanged, if one
> doesn't count `PATH` exports. Thus I don't see the need to attach my
> full config. If anyone has troubles reproducing this, let me know. But
> it should be enough to have ohmyzsh installed.
>

tl;dr: I can reproduce two different segfaults here: one of them with
a minimal reproduction recipe; one with my personal setup, and
a specific setopt makes it go away.

With personal setup with current master:

[[[
% alias a='""'<Left><Left><Left><TAB>
16: ./Src/Zle/compcore.c:1678: expecting 'x' at offset -1 of "x"""
Process 378 stopped
* thread #1, name = 'zsh', stop reason = signal SIGSEGV: invalid address (fault address: 0x7ffff6c84fff)
    frame #0: 0x000000000053046e zsh`check_param(s="\x9e\x9e", set=0, test=1) at compcore.c:1127
   1124      * offset "offs" into it via a global sucks badly.
   1125      */
   1126     for (p = s + offs; ; p--) {
-> 1127         if (*p == String || *p == Qstring) {
   1128             /*
   1129              * String followed by Snull (unquoted) or
   1130              * QString followed by ' (quoted) indicate a nested
(lldb) bt 4
* thread #1, name = 'zsh', stop reason = signal SIGSEGV: invalid address (fault address: 0x7ffff6c84fff)
  * frame #0: 0x000000000053046e zsh`check_param(s="\x9e\x9e", set=0, test=1) at compcore.c:1127
    frame #1: 0x0000000000531c70 zsh`set_comp_sep at compcore.c:1698
    frame #2: 0x000000000052dbba zsh`bin_compset(name="compset", argv=0x00007ffff6c85958, ops=0x00007ffffffd2358, func=0) at complete.c:1140
    frame #3: 0x00000000004216e1 zsh`execbuiltin(args=0x00007ffff6c85928, assigns=0x0000000000000000, bn=0x00000000005e7c60) at builtin.c:507
(lldb) p funcstack[0]
(funcstack) $0 = {
  prev = 0x00007ffffffdbe78
  name = 0x00007ffff6c85160 "_alias"
  filename = 0x00007ffff6c85168 "/srv/zsh/share/zsh/5.8.0.2-dev/functions/_alias"
  caller = 0x00000000005a33b3 "(eval)"
  flineno = 0
  lineno = 1
  tp = 1
}
(lldb) p funcstack[1]
(funcstack) $1 = {
  prev = 0x00007361696c615f
  name = 0x68737a2f7672732f <no value available>
  filename = 0x7a2f65726168732f <no value available>
  caller = 0x302e382e352f6873 <no value available>
  flineno = 7363234093618508334
  lineno = 8317708060514807413
  tp = 1818320687
}
(lldb) p s
(char *) $2 = 0x00007ffff6c85a10 "\x9e\x9e"
(lldb) p offs
(int) $3 = -1
]]]

In frame #0's actual arguments, 0x9e is Dnull.

funcstack[1] appears to be a memory block comprising some strings
(reversed because I'm on a little-endian platform):
.
    % () { for 1; print -r -- $1 "$(xxd -p -r <<<${1#0x})" } 0x00007361696c615f 0x68737a2f7672732f 0x7a2f65726168732f 0x302e382e352f6873
    0x00007361696c615f saila_
    0x68737a2f7672732f hsz/vrs/
    0x7a2f65726168732f z/erahs/
    0x302e382e352f6873 0.8.5/hs
    % xxd -p -r <<<$(printf %x 7363234093618508334); echo
    f/ved-2.
    % xxd -p -r <<<$(printf %x 8317708060514807413); echo
    snoitcnu
    % xxd -p -r <<<$(printf %x 1818320687); echo
    la_/
    (lldb) p (char*) &funcstack[1].filename
    (char *) $0 = 0x00007ffff6c85170 "/share/zsh/5.8.0.2-dev/functions/_alias"

The segfault goes away when I unsetopt completeinword.

From that, I get a minimal segfault with a different backtrace:

[[[
$ lldb -- /srv/zsh/bin/zsh -f
% autoload compinit
% compinit
% setopt completeinword
% alias a='""'<Left><Left><Left><TAB>
16: ./Src/Zle/compcore.c:1678: expecting 'x' at offset -1 of "x"""
Process 3116 stopped
* thread #1, name = 'zsh', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
    frame #0: 0x00007ffff7c74df2 libc.so.6`malloc_consolidate(av=0x00007ffff7db0c40) at malloc.c:4494
(lldb) bt 7
* thread #1, name = 'zsh', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
  * frame #0: 0x00007ffff7c74df2 libc.so.6`malloc_consolidate(av=0x00007ffff7db0c40) at malloc.c:4494
    frame #1: 0x00007ffff7c77a58 libc.so.6`_int_malloc(av=0x00007ffff7db0c40, bytes=2036) at malloc.c:3695
    frame #2: 0x00007ffff7c793e3 libc.so.6`__GI___libc_malloc(bytes=2036) at malloc.c:3049
    frame #3: 0x000000000048d49d zsh`zalloc(size=2036) at mem.c:966
    frame #4: 0x00000000004492ff zsh`getfpfunc(s="_command_names", ksh=0x00007ffffffd0f1c, fdir=0x00007ffffffd0f08, alt_path=0x0000000000000000, test_only=0) at exec.c:6097
    frame #5: 0x0000000000448d96 zsh`loadautofn(shf=0x000000000069c790, fksh=1, autol=0, current_fpath=0) at exec.c:5582
    frame #6: 0x000000000044ef7c zsh`execcmd_exec(state=0x00007ffffffd2b20, eparams=0x00007ffffffd1ae8, input=0, output=0, how=18, last1=2, close_if_forked=-1) at exec.c:3433
(lldb) p funcstack[0]
(funcstack) $0 = {
  prev = 0x00007ffff6cd0128
  name = 0x00007ffff6ccc160 "_command_names"
  filename = 0x0000000000000000 <no value available>
  caller = 0x00007ffff6cd0160 "_autocd"
  flineno = 0
  lineno = 3
  tp = 1
}
(lldb) p funcstack[1]
(funcstack) $1 = {
  prev = 0x646e616d6d6f635f
  name = 0x000073656d616e5f <no value available>
  filename = 0x636f6c2f7273752f <no value available>
  caller = 0x65726168732f6c61 <no value available>
  flineno = 8388362428407314991
  lineno = 7598807797348052325
  tp = 779316847
}
(lldb) 
]]]

    % () { for 1; print -r -- $1 "$(xxd -p -r <<<${1#0x})" } 0x646e616d6d6f635f 0x000073656d616e5f 0x636f6c2f7273752f 0x65726168732f6c61
    0x646e616d6d6f635f dnammoc_
    0x000073656d616e5f seman_
    0x636f6c2f7273752f col/rsu/
    0x65726168732f6c61 erahs/la
    % for 1 in 8388362428407314991 7598807797348052325 779316847 ; { xxd -p -r <<<$(printf %x $1) | xxd }
    00000000: 7469 732f 6873 7a2f                      tis/hsz/
    00000000: 6974 636e 7566 2d65                      itcnuf-e
    00000000: 2e73 6e6f                                .sno
    (lldb) p (char*)&funcstack[1].filename
    (char *) $6 = 0x00007ffff6ccc170 "/usr/local/share/zsh/site-functions.zwc"

I can reproduce this latter variant in 5.7.1 as well.  I haven't tried
older versions.

I haven't tried reproducing the issue under ohmyzsh.  It could in
theory be a different one.

Petr, could you please check whether the issue reproduces if you
«unsetopt completeinword»?

Cheers,

Daniel



Messages sorted by: Reverse Date, Date, Thread, Author