From a555069b7d790abedc60edc505bd35bda257949d Mon Sep 17 00:00:00 2001 From: mityu Date: Sat, 25 Nov 2023 15:41:20 +0100 Subject: patch 9.0.2129: [security]: use-after-free in call_dfunc() Problem: [security]: use-after-free in call_dfunc() Solution: Refresh dfunc pointer closes: #13571 This Commit fixes a SEGV caused by a use-after-free bug in call_dfunc(). When calling check_ufunc_arg_types() from the call_dfunc() it may cause def functions to be re-compiled and if there are too many def functions, the def_functions array will be re-allocated. Which means, that the dfunc pointer in call_dfunc() now starts pointing to freed memory. So we need to reset the dfunc pointer after calling check_ufunc_arg_types(). Let's also add a test, to ensure we do not regress. Signed-off-by: mityu Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_class.vim | 167 ++++++++++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9execute.c | 6 ++ 3 files changed, 175 insertions(+) (limited to 'src') diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 18fde09910..3da4eb00f3 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -8585,4 +8585,171 @@ def Test_dict_member_key_type_check() v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got dict', 3) enddef +def Test_compile_many_def_functions_in_funcref_instr() + # This used to crash Vim. This is reproducible only when run on new instance + # of Vim. + var lines =<< trim END + vim9script + + class A + def new() + this.TakeFunc(this.F00) + enddef + + def TakeFunc(F: func) + enddef + + def F00() + this.F01() + this.F02() + this.F03() + this.F04() + this.F05() + this.F06() + this.F07() + this.F08() + this.F09() + this.F10() + this.F11() + this.F12() + this.F13() + this.F14() + this.F15() + this.F16() + this.F17() + this.F18() + this.F19() + this.F20() + this.F21() + this.F22() + this.F23() + this.F24() + this.F25() + this.F26() + this.F27() + this.F28() + this.F29() + this.F30() + this.F31() + this.F32() + this.F33() + this.F34() + this.F35() + this.F36() + this.F37() + this.F38() + this.F39() + this.F40() + this.F41() + this.F42() + this.F43() + this.F44() + this.F45() + this.F46() + this.F47() + enddef + + def F01() + enddef + def F02() + enddef + def F03() + enddef + def F04() + enddef + def F05() + enddef + def F06() + enddef + def F07() + enddef + def F08() + enddef + def F09() + enddef + def F10() + enddef + def F11() + enddef + def F12() + enddef + def F13() + enddef + def F14() + enddef + def F15() + enddef + def F16() + enddef + def F17() + enddef + def F18() + enddef + def F19() + enddef + def F20() + enddef + def F21() + enddef + def F22() + enddef + def F23() + enddef + def F24() + enddef + def F25() + enddef + def F26() + enddef + def F27() + enddef + def F28() + enddef + def F29() + enddef + def F30() + enddef + def F31() + enddef + def F32() + enddef + def F33() + enddef + def F34() + enddef + def F35() + enddef + def F36() + enddef + def F37() + enddef + def F38() + enddef + def F39() + enddef + def F40() + enddef + def F41() + enddef + def F42() + enddef + def F43() + enddef + def F44() + enddef + def F45() + enddef + def F46() + enddef + def F47() + enddef + endclass + + A.new() + END + writefile(lines, 'Xscript', 'D') + g:RunVim([], [], '-u NONE -S Xscript -c qa') + assert_equal(0, v:shell_error) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index a9d0bdd2b4..ab8492840d 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2129, /**/ 2128, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 997dfa00ab..e329559eab 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -548,6 +548,12 @@ call_dfunc( if (check_ufunc_arg_types(ufunc, argcount, vararg_count, ectx) == FAIL) return FAIL; + // While check_ufunc_arg_types call, def function compilation process may + // run. If so many def functions are compiled, def_functions array may be + // reallocated and dfunc may no longer have valid pointer. Get the object + // pointer from def_functions again here. + dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx; + // Reserve space for: // - missing arguments // - stack frame -- cgit v1.2.3