Dominique Pellé <[email protected]> wrote:

> Christian Brabandt wrote:
>
>> Bram,
>> I tend to abort long running z= commands by pressing <Ctrl-C>. This
>> randomly crashed Vim for me, but until now, I couldn't reliably
>> reproduce it, possibly it is timing dependent or influenced by some
>> setting, I don't know.
>>
>> However, I could just get a stacktrace as well as some valgrind logfiles
>> (see attached tar.xz file). Both logfiles are not very helpful. It seems
>> like Vim is allocating a lot of memory and finally gets killed. The
>> problem lies in the the add_sound_suggest() function and it seems, wlen
>> runs over. When gdb caught SEGV, it was at some value of above 21000.
>>
>> Here is a patch, that makes sure, wlen doesn't increment over MAXWLEN
>> and fixes it for me. So please include it.
>>
>>
>> diff --git a/src/spell.c b/src/spell.c
>> --- a/src/spell.c
>> +++ b/src/spell.c
>> @@ -13382,6 +13382,10 @@
>>         wordcount = 0;
>>         for (;;)
>>         {
>> +           if (wlen > MAXWLEN)
>> +               /* safety check */
>> +               break;
>> +
>>             i = 1;
>>             if (wordcount == orgnr && byts[n + 1] == NUL)
>>                 break;  /* found end of word */
>
>
> Trying to press CTRL-C right after  z=  as you describe, it
> did not take me long to get a crash as well using vim-7.4.92
> on Linux x86_84. This is the error I got with valgrind, which
> is different than yours:
>
> ==3900== Memcheck, a memory error detector
> ==3900== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
> ==3900== Using Valgrind-3.9.0.SVN and LibVEX; rerun with -h for copyright info
> ==3900== Command: ./vim spell.c
> ==3900== Parent PID: 3839
> ==3900==
> ==3900== Invalid read of size 1
> ==3900==    at 0x56CE37: spell_edit_score (spell.c:15072)
> ==3900==    by 0x571913: suggest_trie_walk (spell.c:13504)
> ==3900==    by 0x568365: spell_find_suggest (spell.c:13277)
> ==3900==    by 0x566F3E: spell_suggest (spell.c:10245)
> ==3900==    by 0x4DDD8D: nv_zet (normal.c:5272)
> ==3900==    by 0x4D4460: normal_cmd (normal.c:1200)
> ==3900==    by 0x5C1872: main_loop (main.c:1329)
> ==3900==    by 0x5C0DF1: main (main.c:1020)
> ==3900==  Address 0xf00000001 is not stack'd, malloc'd or (recently) free'd
> ==3900==
>
> It happened with  spelllang=fr, when pressing z= on the
> word  x  (only one letter x).
>
> It does not happen all the time, but it's easy to reproduce
> after trying several times, always with the same stack trace
> as above and with the same invalid address 0xf00000001
>
> Code where it crashes:
>
> 15048     static int
> 15049 spell_edit_score(slang, badword, goodword)
> .....
> 15069         for (p = badword, badlen = 0; *p != NUL; )
> 15070             wbadword[badlen++] = mb_cptr2char_adv(&p);
>
> It must be p which is wrong (i.e. parameter badword).
>
>
> Trying also with clang-3.4 address sanitizer and undefined
> sanitizer (clang -fsanituze=address,undefined) shows a
> stack buffer overflow (such stack overflows can't be detected
> by valgrind but are detected by the address sanitizer):
>
> ==4531==ERROR: AddressSanitizer: stack-buffer-overflow on address
> 0x7fff0f0404ba at pc 0x1a05fd3 bp 0x7fff0f03f970 sp 0x7fff0f03f968
> WRITE of size 1 at 0x7fff0f0404ba thread T0
>     #0 0x1a05fd2 in add_sound_suggest /home/pel/sb/vim/src/spell.c:13429
>     #1 0x19c7ab7 in suggest_trie_walk /home/pel/sb/vim/src/spell.c:11729
>     #2 0x19b6121 in suggest_try_soundalike /home/pel/sb/vim/src/spell.c:13277
>     #3 0x198b7c5 in spell_suggest_intern /home/pel/sb/vim/src/spell.c:10876
>     #4 0x1947750 in spell_find_suggest /home/pel/sb/vim/src/spell.c:10711
>     #5 0x193d3b8 in spell_suggest /home/pel/sb/vim/src/spell.c:10245
>     #6 0x127a543 in nv_zet /home/pel/sb/vim/src/normal.c:5272
>     #7 0x119fb5b in normal_cmd /home/pel/sb/vim/src/normal.c:1200
>     #8 0x1fcbb64 in main_loop /home/pel/sb/vim/src/main.c:1329
>     #9 0x1faa79c in main /home/pel/sb/vim/src/main.c:1020
>     #10 0x7fba1dd10de4 in __libc_start_main
> /build/buildd/eglibc-2.17/csu/libc-start.c:260
>     #11 0x4a880c in _start (/home/pel/sb/vim/src/vim+0x4a880c)
>
> Address 0x7fff0f0404ba is located in stack of thread T0 at offset 794 in frame
>     #0 0x1a0273f in add_sound_suggest /home/pel/sb/vim/src/spell.c:13328
>
>   This frame has 26 object(s):
>     [32, 40) 'su.addr'
>     [96, 104) 'goodword.addr'
>     [160, 164) 'score.addr'
>     [224, 232) 'lp.addr'
>     [288, 296) 'slang'
>     [352, 356) 'sfwordnr'
>     [416, 424) 'nrline'
>     [480, 484) 'orgnr'
>     [544, 794) 'theword' <== Memory access at offset 794 overflows this 
> variable
>     [832, 836) 'i'
>     [896, 900) 'wlen'
>     [960, 968) 'byts'
>     [1024, 1032) 'idxs'
>     [1088, 1092) 'n'
>     [1152, 1156) 'wordcount'
>     [1216, 1220) 'wc'
>     [1280, 1284) 'goodscore'
>     [1344, 1352) 'hash'
>     [1408, 1416) 'hi'
>     [1472, 1480) 'sft'
>     [1536, 1540) 'bc'
>     [1600, 1604) 'gc'
>     [1664, 1668) 'limit'
>     [1728, 1978) 'cword'
>     [2016, 2024) 'p'
>     [2080, 2084) 'flags'
> HINT: this may be a false positive if your program uses some custom
> stack unwind mechanism or swapcontext
>       (longjmp and C++ exceptions *are* supported)
> SUMMARY: AddressSanitizer: stack-buffer-overflow
> /home/pel/sb/vim/src/spell.c:13429 add_sound_suggest
> Shadow bytes around the buggy address:
>   0x100061e00040: 00 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2
>   0x100061e00050: 00 f4 f4 f4 f2 f2 f2 f2 00 f4 f4 f4 f2 f2 f2 f2
>   0x100061e00060: 04 f4 f4 f4 f2 f2 f2 f2 00 f4 f4 f4 f2 f2 f2 f2
>   0x100061e00070: 04 f4 f4 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00
>   0x100061e00080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x100061e00090: 00 00 00 00 00 00 00[02]f2 f2 f2 f2 04 f4 f4 f4
>   0x100061e000a0: f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2 00 f4 f4 f4
>   0x100061e000b0: f2 f2 f2 f2 00 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4
>   0x100061e000c0: f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4
>   0x100061e000d0: f2 f2 f2 f2 04 f4 f4 f4 f2 f2 f2 f2 00 f4 f4 f4
>   0x100061e000e0: f2 f2 f2 f2 00 f4 f4 f4 f2 f2 f2 f2 00 f4 f4 f4
> Shadow byte legend (one shadow byte represents 8 application bytes):
>   Addressable:           00
>   Partially addressable: 01 02 03 04 05 06 07
>   Heap left redzone:     fa
>   Heap right redzone:    fb
>   Freed heap region:     fd
>   Stack left redzone:    f1
>   Stack mid redzone:     f2
>   Stack right redzone:   f3
>   Stack partial redzone: f4
>   Stack after return:    f5
>   Stack use after scope: f8
>   Global redzone:        f9
>   Global init order:     f6
>   Poisoned by user:      f7
>   ASan internal:         fe
> ==4531==ABORTING
>
> 13322     static void
> 13323 add_sound_suggest(su, goodword, score, lp)
> 13324     suginfo_T   *su;
> 13325     char_u      *goodword;
> 13326     int         score;          /* soundfold score  */
> 13327     langp_T     *lp;
> 13328 {
> 13329     slang_T     *slang = lp->lp_slang;  /* language for sound folding */
> 13330     int         sfwordnr;
> 13331     char_u      *nrline;
> 13332     int         orgnr;
> 13333     char_u      theword[MAXWLEN];
> 13334     int         i;
> 13335     int         wlen;
> 13336     char_u      *byts;
> 13337     idx_T       *idxs;
> 13338     int         n;
> 13339     int         wordcount;
> 13340     int         wc;
> 13341     int         goodscore;
> 13342     hash_T      hash;
> 13343     hashitem_T  *hi;
> 13344     sftword_T   *sft;
> 13345     int         bc, gc;
> 13346     int         limit;
> ......
> 13429             theword[wlen++] = byts[n + i];
> 13430             n = idxs[n + i];
>
> So it looks theword[wlen++] writes more than 250 bytes
> in a 250 byte array in the stack.
>
> But in your patch, I think that the  line "if (wlen > MAXWLEN)"
> should really be ""if (wlen >= MAXWLEN - 1)".  That seems to
> fix it:
>
> $ hg diff spell.c
> diff -r 0c37f66b4f3b src/spell.c
> --- a/src/spell.c    Thu Nov 14 05:48:46 2013 +0100
> +++ b/src/spell.c    Mon Nov 18 22:28:25 2013 +0100
> @@ -13402,6 +13402,10 @@
>      wordcount = 0;
>      for (;;)
>      {
> +            if (wlen >= MAXWLEN - 1)
> +                /* safety check */
> +                break;
> +
>          i = 1;
>          if (wordcount == orgnr && byts[n + 1] == NUL)
>          break;    /* found end of word */
>
>
> The question remains: what is wlen so large when
> pressing CTRL-C?  I do not know yet.


Although the above patch fixes it in practice for me,
I now see that the safety should actually be
even stricter, since in the for (...) loop, there is
this statement at spell.c:13416:

    STRCPY(theword + wlen, "BAD");

... which writes 4 bytes "BAD\0" from &theword[wlen]
if executed.

But it it's odd that it writes "BAD\0" becase then at line 13416
because it gets immediately overwritten with a \0
after the loop at line 13433, so it becomes "\0AD\0".
That does not make sense to me:

13403         for (;;)
13404         {
13405             i = 1;
13406             if (wordcount == orgnr && byts[n + 1] == NUL)
13407                 break;  /* found end of word */
13408
13409             if (byts[n + 1] == NUL)
13410                 ++wordcount;
13411
13412             /* skip over the NUL bytes */
13413             for ( ; byts[n + i] == NUL; ++i)
13414                 if (i > byts[n])        /* safety check */
13415                 {
13416                     STRCPY(theword + wlen, "BAD");
13417                     goto badword;
13418                 }
13419
13420             /* One of the siblings must have the word. */
13421             for ( ; i < byts[n]; ++i)
13422             {
13423                 wc = idxs[idxs[n + i]]; /* nr of words under this byte */
13424                 if (wordcount + wc > orgnr)
13425                     break;
13426                 wordcount += wc;
13427             }
13428
13429             theword[wlen++] = byts[n + i];
13430             n = idxs[n + i];
13431         }
13432 badword:
13433         theword[wlen] = NUL;

Perhaps a better patch is something like this,
but I admit that I don't understand this piece code:

$ hg diff spell.c
diff -r 0c37f66b4f3b src/spell.c
--- a/src/spell.c    Thu Nov 14 05:48:46 2013 +0100
+++ b/src/spell.c    Mon Nov 18 22:55:02 2013 +0100
@@ -13402,6 +13402,10 @@
     wordcount = 0;
     for (;;)
     {
+        if (wlen > MAXWLEN - 4)
+        /* safety check */
+        break;
+
         i = 1;
         if (wordcount == orgnr && byts[n + 1] == NUL)
         break;    /* found end of word */
@@ -13414,6 +13418,7 @@
         if (i > byts[n])    /* safety check */
         {
             STRCPY(theword + wlen, "BAD");
+            wlen += 3;
             goto badword;
         }

Regards
Dominique

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Raspunde prin e-mail lui