There is a possibility of Vim crashing when a variable or an option
('omnifunc' or 'completefunc') is unset or has its value modified.
The crash is due to an invalid memory access, which is reported by
Valgrind as follows:
#v+
==19657== Invalid write of size 1
==19657== at 0x8077102: call_func (eval.c:8208)
==19657== by 0x8076BA9: get_func_tv (eval.c:7976)
==19657== by 0x8073188: eval7 (eval.c:5023)
==19657== by 0x8072AA1: eval6 (eval.c:4690)
==19657== by 0x8072697: eval5 (eval.c:4506)
==19657== by 0x8071C41: eval4 (eval.c:4201)
==19657== by 0x8071AAF: eval3 (eval.c:4113)
==19657== by 0x8071951: eval2 (eval.c:4042)
==19657== by 0x80717A2: eval1 (eval.c:3967)
==19657== by 0x8086DD6: ex_echo (eval.c:19459)
==19657== by 0x80A1150: do_one_cmd (ex_docmd.c:2629)
==19657== by 0x809EA29: do_cmdline (ex_docmd.c:1098)
==19657== by 0x809CCD4: do_source (ex_cmds2.c:3204)
==19657== by 0x809C4FF: cmd_source (ex_cmds2.c:2809)
==19657== by 0x809C457: ex_source (ex_cmds2.c:2782)
==19657== by 0x80A1150: do_one_cmd (ex_docmd.c:2629)
==19657== by 0x809EA29: do_cmdline (ex_docmd.c:1098)
==19657== by 0x809E0E3: do_cmdline_cmd (ex_docmd.c:704)
==19657== by 0x80E2572: exe_commands (main.c:2733)
==19657== by 0x80DFF26: main (main.c:888)
==19657== Address 0x4e17343 is 11 bytes inside a block of size 12 free'd
==19657== at 0x4023836: free (vg_replace_malloc.c:325)
==19657== by 0x810E0EC: vim_free (misc2.c:1647)
==19657== by 0x80858FA: clear_tv (eval.c:18562)
==19657== by 0x80862A8: delete_var (eval.c:19029)
==19657== by 0x8070E6A: do_unlet (eval.c:3555)
==19657== by 0x8070CB8: do_unlet_var (eval.c:3499)
==19657== by 0x8070BC3: ex_unletlock (eval.c:3462)
==19657== by 0x8070A22: ex_unlet (eval.c:3400)
==19657== by 0x80A1150: do_one_cmd (ex_docmd.c:2629)
==19657== by 0x809EA29: do_cmdline (ex_docmd.c:1098)
==19657== by 0x808ABDC: call_user_func (eval.c:21332)
==19657== by 0x8076F55: call_func (eval.c:8130)
==19657== by 0x8076BA9: get_func_tv (eval.c:7976)
==19657== by 0x8073188: eval7 (eval.c:5023)
==19657== by 0x8072AA1: eval6 (eval.c:4690)
==19657== by 0x8072697: eval5 (eval.c:4506)
==19657== by 0x8071C41: eval4 (eval.c:4201)
==19657== by 0x8071AAF: eval3 (eval.c:4113)
==19657== by 0x8071951: eval2 (eval.c:4042)
==19657== by 0x80717A2: eval1 (eval.c:3967)
==19657== by 0x8086DD6: ex_echo (eval.c:19459)
==19657== by 0x80A1150: do_one_cmd (ex_docmd.c:2629)
==19657== by 0x809EA29: do_cmdline (ex_docmd.c:1098)
==19657== by 0x809CCD4: do_source (ex_cmds2.c:3204)
==19657== by 0x809C4FF: cmd_source (ex_cmds2.c:2809)
==19657== by 0x809C457: ex_source (ex_cmds2.c:2782)
==19657== by 0x80A1150: do_one_cmd (ex_docmd.c:2629)
==19657== by 0x809EA29: do_cmdline (ex_docmd.c:1098)
==19657== by 0x809E0E3: do_cmdline_cmd (ex_docmd.c:704)
==19657== by 0x80E2572: exe_commands (main.c:2733)
==19657== by 0x80DFF26: main (main.c:888)
#v-
The invalid memory access can be triggered by executing the following
script:
#v+
func FuncWithRef(a)
unlet g:FuncRef
return a:a
endfunc
let g:FuncRef=function("FuncWithRef")
echo g:FuncRef(333)
q
#v-
The attached patch fixes the problem and modifies test 34 to cause the
freed memory.
--
Cheers,
Lech
--
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
diff --git a/src/eval.c b/src/eval.c
index be59f98..caa3224 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -8023,26 +8023,36 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
int i;
int llen;
ufunc_T *fp;
- int cc;
#define FLEN_FIXED 40
char_u fname_buf[FLEN_FIXED + 1];
char_u *fname;
+ char_u *name_save;
+
+ /* NOTE that if "name" comes from a funcref variable, while evaluating the
+ * function it is possible that the memory pointed to by "name" will be
+ * freed as a result of executing e.g.
+ * unlet FuncRefVariable
+ * Let's make a copy of "name" to be able to modify the string to heart's
+ * content without having to restore it afterwards.
+ */
+ name_save = vim_strsave(name);
+ if (name_save == NULL)
+ return FAIL;
/*
* In a script change <SID>name() and s:name() to K_SNR 123_name().
* Change <SNR>123_name() to K_SNR 123_name().
* Use fname_buf[] when it fits, otherwise allocate memory (slow).
*/
- cc = name[len];
- name[len] = NUL;
- llen = eval_fname_script(name);
+ name_save[len] = NUL;
+ llen = eval_fname_script(name_save);
if (llen > 0)
{
fname_buf[0] = K_SPECIAL;
fname_buf[1] = KS_EXTRA;
fname_buf[2] = (int)KE_SNR;
i = 3;
- if (eval_fname_sid(name)) /* "<SID>" or "s:" */
+ if (eval_fname_sid(name_save)) /* "<SID>" or "s:" */
{
if (current_SID <= 0)
error = ERROR_SCRIPT;
@@ -8052,25 +8062,25 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
i = (int)STRLEN(fname_buf);
}
}
- if (i + STRLEN(name + llen) < FLEN_FIXED)
+ if (i + STRLEN(name_save + llen) < FLEN_FIXED)
{
- STRCPY(fname_buf + i, name + llen);
+ STRCPY(fname_buf + i, name_save + llen);
fname = fname_buf;
}
else
{
- fname = alloc((unsigned)(i + STRLEN(name + llen) + 1));
+ fname = alloc((unsigned)(i + STRLEN(name_save + llen) + 1));
if (fname == NULL)
error = ERROR_OTHER;
else
{
mch_memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ STRCPY(fname + i, name_save + llen);
}
}
}
else
- fname = name;
+ fname = name_save;
*doesrange = FALSE;
@@ -8185,29 +8195,29 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
switch (error)
{
case ERROR_UNKNOWN:
- emsg_funcname(N_("E117: Unknown function: %s"), name);
+ emsg_funcname(N_("E117: Unknown function: %s"), name_save);
break;
case ERROR_TOOMANY:
- emsg_funcname(e_toomanyarg, name);
+ emsg_funcname(e_toomanyarg, name_save);
break;
case ERROR_TOOFEW:
emsg_funcname(N_("E119: Not enough arguments for function: %s"),
- name);
+ name_save);
break;
case ERROR_SCRIPT:
emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
- name);
+ name_save);
break;
case ERROR_DICT:
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
- name);
+ name_save);
break;
}
}
- name[len] = cc;
- if (fname != name && fname != fname_buf)
+ if (fname != name_save && fname != fname_buf)
vim_free(fname);
+ vim_free(name_save);
return ret;
}
diff --git a/src/testdir/test34.in b/src/testdir/test34.in
index 5eef715..28fd00c 100644
--- a/src/testdir/test34.in
+++ b/src/testdir/test34.in
@@ -35,6 +35,11 @@ STARTTEST
: let g:counter = 0
: return ''
:endfunc
+:func FuncWithRef(a)
+: unlet g:FuncRef
+: return a:a
+:endfunc
+:let g:FuncRef=function("FuncWithRef")
:let counter = 0
:inoremap <expr> ( ListItem()
:inoremap <expr> [ ListReset()
@@ -47,6 +52,7 @@ C=Table("xxx", 4, "asdf")
=retval
=Compute(45, 5, "retval")
=retval
+ =g:FuncRef(333)
XX+-XX
---*---
diff --git a/src/testdir/test34.ok b/src/testdir/test34.ok
index 368ae10..951dd8b 100644
--- a/src/testdir/test34.ok
+++ b/src/testdir/test34.ok
@@ -1,4 +1,4 @@
-xxx4asdf fail nop ok 9
+xxx4asdf fail nop ok 9 333
XX111XX
---222---
1. one