segmentation fault in regexp.c:1788 in vim/vim

Valid

Reported on

Feb 21st 2023


Description

SIGSEGV raised on regtilde function at regexp.c. As the function processes the tainted string inside the poc file, constant calls to the alloc function with ever-increasing size actually exhausts memory and the process terminates. At last negative size value is assigned.

Version

$ git log
commit 938ae280c79b8cdb0fca60336ec4c090ecd8bb5a (HEAD -> master, origin/master, origin/HEAD)
Author: Bram Moolenaar <Bram@vim.org>
Date:   Mon Feb 20 20:44:55 2023 +0000

    Update runtime files.

Proof of Concept

$ ./vim -u NONE -i NONE -n -m -X -Z -e -s -S poc -c :qa!
Segmentation fault (core dumped)

poc

file poc2 achieves cpu and memory high by handling very large memory. This is handled in second memmove at regexp.c:1790
poc2

ASAN

$ ./vim -u NONE -i NONE -n -m -X -Z -e -s -S poc -c :qa!
=================================================================
==12373==ERROR: AddressSanitizer: negative-size-param: (size=-2145058819)
    #0 0x555c98d1cfdc in __asan_memmove (/home/user/vim/src/vim+0x250fdc) (BuildId: 114ec797266b9b8572c771e928386eb985250bed)
    #1 0x555c992bc4ad in regtilde /home/user/vim/src/regexp.c:1788:7
    #2 0x555c98f4b2e3 in ex_substitute /home/user/vim/src/ex_cmds.c:4047:19
    #3 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #4 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #5 0x555c991a9c61 in nv_colon /home/user/vim/src/normal.c:3176:15
    #6 0x555c9918d86a in normal_cmd /home/user/vim/src/normal.c:938:5
    #7 0x555c98f8da9c in exec_normal /home/user/vim/src/ex_docmd.c:8887:6
    #8 0x555c98f8d6c3 in exec_normal_cmd /home/user/vim/src/ex_docmd.c:8850:5
    #9 0x555c98f8d431 in ex_normal /home/user/vim/src/ex_docmd.c:8768:6
    #10 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #11 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #12 0x555c99384eb3 in do_source_ext /home/user/vim/src/scriptfile.c:1759:5
    #13 0x555c993829e0 in do_source /home/user/vim/src/scriptfile.c:1905:12
    #14 0x555c9938252f in cmd_source /home/user/vim/src/scriptfile.c:1250:14
    #15 0x555c9938202d in ex_source /home/user/vim/src/scriptfile.c:1276:2
    #16 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #17 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #18 0x555c98f5f780 in do_cmdline_cmd /home/user/vim/src/ex_docmd.c:587:12
    #19 0x555c9979b46c in exe_commands /home/user/vim/src/main.c:3146:2
    #20 0x555c9979926a in vim_main2 /home/user/vim/src/main.c:782:2
    #21 0x555c99792e7e in main /home/user/vim/src/main.c:433:12
    #22 0x7fbdd0a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #23 0x7fbdd0a29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #24 0x555c98c9aa74 in _start (/home/user/vim/src/vim+0x1cea74) (BuildId: 114ec797266b9b8572c771e928386eb985250bed)

0x7fbd09009800 is located 0 bytes inside of 2149908494-byte region [0x7fbd09009800,0x7fbd8925980e)
allocated by thread T0 here:
    #0 0x555c98d1d8be in __interceptor_malloc (/home/user/vim/src/vim+0x2518be) (BuildId: 114ec797266b9b8572c771e928386eb985250bed)
    #1 0x555c98d589d6 in lalloc /home/user/vim/src/alloc.c:246:11
    #2 0x555c98d58939 in alloc /home/user/vim/src/alloc.c:151:12
    #3 0x555c992bc47f in regtilde /home/user/vim/src/regexp.c:1783:12
    #4 0x555c98f4b2e3 in ex_substitute /home/user/vim/src/ex_cmds.c:4047:19
    #5 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #6 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #7 0x555c991a9c61 in nv_colon /home/user/vim/src/normal.c:3176:15
    #8 0x555c9918d86a in normal_cmd /home/user/vim/src/normal.c:938:5
    #9 0x555c98f8da9c in exec_normal /home/user/vim/src/ex_docmd.c:8887:6
    #10 0x555c98f8d6c3 in exec_normal_cmd /home/user/vim/src/ex_docmd.c:8850:5
    #11 0x555c98f8d431 in ex_normal /home/user/vim/src/ex_docmd.c:8768:6
    #12 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #13 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #14 0x555c99384eb3 in do_source_ext /home/user/vim/src/scriptfile.c:1759:5
    #15 0x555c993829e0 in do_source /home/user/vim/src/scriptfile.c:1905:12
    #16 0x555c9938252f in cmd_source /home/user/vim/src/scriptfile.c:1250:14
    #17 0x555c9938202d in ex_source /home/user/vim/src/scriptfile.c:1276:2
    #18 0x555c98f688bf in do_one_cmd /home/user/vim/src/ex_docmd.c:2580:2
    #19 0x555c98f5c684 in do_cmdline /home/user/vim/src/ex_docmd.c:993:17
    #20 0x555c98f5f780 in do_cmdline_cmd /home/user/vim/src/ex_docmd.c:587:12
    #21 0x555c9979b46c in exe_commands /home/user/vim/src/main.c:3146:2
    #22 0x555c9979926a in vim_main2 /home/user/vim/src/main.c:782:2
    #23 0x555c99792e7e in main /home/user/vim/src/main.c:433:12
    #24 0x7fbdd0a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: negative-size-param (/home/user/vim/src/vim+0x250fdc) (BuildId: 114ec797266b9b8572c771e928386eb985250bed) in __asan_memmove
==12373==ABORTING

GDB before memmove function call

  • After a large size alloc function call completes
[----------------------------------registers-----------------------------------]
RAX: 0x7ffec333d010 --> 0x0 
RBX: 0x0 
RCX: 0x7fff5cf9e010 ("nXnXnXnXnXnXnXnXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(\\|0rOg@P(nXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg"...)
RDX: 0xffffffff8024fffd 
RSI: 0x7fff5cf9e010 ("nXnXnXnXnXnXnXnXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(\\|0rOg@P(nXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg"...)
RDI: 0x7ffec333d010 --> 0x0 
RBP: 0x7fffffffb690 --> 0x7fffffffb9b0 --> 0x7fffffffbc30 --> 0x7fffffffc3c0 --> 0x7fffffffc400 --> 0x7fffffffc4b0 (--> ...)
RSP: 0x7fffffffb660 --> 0x1558893e0 
RIP: 0x555555718887 (<regtilde+212>:    call   0x55555558b4e0 <memmove@plt>)
R8 : 0x7ffec333d010 --> 0x0 
R9 : 0x7ffec333d010 --> 0x0 
R10: 0x22 ('"')
R11: 0x246 
R12: 0x7fffffffde98 --> 0x7fffffffe262 ("/home/user/fuzzing/vanillavim/vim/src/vim")
R13: 0x5555558893e0 (<main>:    endbr64)
R14: 0x555555904038 --> 0x55555558bac0 (<__do_global_dtors_aux>:    endbr64)
R15: 0x7ffff7ffd040 --> 0x7ffff7ffe2e0 --> 0x555555554000 --> 0x10102464c457f
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x55555571887d <regtilde+202>:   mov    rax,QWORD PTR [rbp-0x8]
   0x555555718881 <regtilde+206>:   mov    rsi,rcx
   0x555555718884 <regtilde+209>:   mov    rdi,rax
=> 0x555555718887 <regtilde+212>:   call   0x55555558b4e0 <memmove@plt>
   0x55555571888c <regtilde+217>:   mov    eax,DWORD PTR [rbp-0x20]
   0x55555571888f <regtilde+220>:   movsxd rdx,eax
   0x555555718892 <regtilde+223>:   mov    rax,QWORD PTR [rip+0x2320f7]        # 0x55555594a990 <reg_prev_sub>
   0x555555718899 <regtilde+230>:   mov    ecx,DWORD PTR [rbp-0x1c]
Guessed arguments:
arg[0]: 0x7ffec333d010 --> 0x0 
arg[1]: 0x7fff5cf9e010 ("nXnXnXnXnXnXnXnXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(\\|0rOg@P(nXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg"...)
arg[2]: 0xffffffff8024fffd 
arg[3]: 0x7fff5cf9e010 ("nXnXnXnXnXnXnXnXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(\\|0rOg@P(nXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg"...)
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffb660 --> 0x1558893e0 
0008| 0x7fffffffb668 --> 0x5555559677e0 ("nX", '~' <repeats 12 times>, "\\|0rOg@P(")
0016| 0x7fffffffb670 --> 0x8024fffd19a0ffff 
0024| 0x7fffffffb678 --> 0x7fff5cf9e010 ("nXnXnXnXnXnXnXnXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(\\|0rOg@P(nXnX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg@P(nX\\|0rOg"...)
0032| 0x7fffffffb680 --> 0x7fffdd1ee00d ("~~~~~~~\\|0rOg@P(")
0040| 0x7fffffffb688 --> 0x7ffec333d010 --> 0x0 
0048| 0x7fffffffb690 --> 0x7fffffffb9b0 --> 0x7fffffffbc30 --> 0x7fffffffc3c0 --> 0x7fffffffc400 --> 0x7fffffffc4b0 (--> ...)
0056| 0x7fffffffb698 --> 0x55555561996e (<ex_substitute+2947>:  mov    QWORD PTR [rbp-0x1c0],rax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Impact

it can lead to DoS, can affect other processes by exhausting memory, dangerous from heap attacks, possibly remote execution

We are processing your report and will contact the vim team within 24 hours. 3 months ago
thkim0 modified the report
3 months ago
thkim0 modified the report
3 months ago
thkim0 modified the report
3 months ago
We have contacted a member of the vim team and are waiting to hear back 3 months ago
thkim0
3 months ago

Researcher


@maintainer Is the poc here too long? This vulnerability uses a string inside the file. If the poc is too long, I'll trim it.

Bram Moolenaar
3 months ago

Maintainer


Please always try to reduce the size of the POC. Certainly in this case, as the script contains many control characters and random text.

thkim0
3 months ago

Researcher


Okay i will. Here's the first trimmed version poc2

here is short explained for this vuln.
The very large difference in values in len = (int)(p - newsub) in regexp.c:1787 is assigned to the len variable, and the very large value with the high order bits filled with 0xff is passed as an argument to the memmove function due to the (size_t) cast in mch_memmove(tmpsub, newsub, (size_t)len) in regexp.c:1788. This is treated as negative inside the memmove function, causing a SIGSEGV signal to be raised and the process to terminate.

thkim0
3 months ago

Researcher


little bit more trimmed version may take a little while to poc poc3

thkim0
2 months ago

Researcher


The POC takes a bit of time because it involves constant heap allocation and freeing. In my case, it always resulted in a sigfault within 10 seconds

Bram Moolenaar validated this vulnerability 16 days ago

Sorry for the long delay. I tried out "poc3" and could reproduce the problem. But it does take a very long time before it fails. That makes it unsuitable for a regression test. Any idea of how to trigger the issue in a quicker way?

thkim0 has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Bram Moolenaar marked this as fixed in 9.0.1532 with commit ab9a2d 16 days ago
Bram Moolenaar has been awarded the fix bounty
This vulnerability has been assigned a CVE
Bram Moolenaar published this vulnerability 16 days ago
regexp.c#L1788 has been validated
regexp.c#L1790 has been validated
to join this conversation