From a66fce6fabc290678044bca9efdef4e112fa44ab Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 12 Dec 2025 08:14:58 +0800 Subject: [PATCH] vim-patch:98a0cbf: patch 9.1.1971: crash with invalid positional argument 0 in printf() (#36919) Problem: crash with invalid positional argument 0 in printf() Solution: Reject positional arguments <= 0. closes: vim/vim#18898 https://github.com/vim/vim/commit/98a0cbf05bd45fa6c4ede7b791fd21760bc587c8 Co-authored-by: Christian Brabandt --- runtime/doc/vimfn.txt | 12 ++++++------ runtime/lua/vim/_meta/vimfn.lua | 12 ++++++------ src/nvim/eval.lua | 12 ++++++------ src/nvim/strings.c | 5 +++++ test/old/testdir/test_format.vim | 2 ++ 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index 51e23153da..8f52e14b46 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -7419,24 +7419,24 @@ printf({fmt}, {expr1} ...) *printf()* < *E1502* You can re-use a [field-width] (or [precision]) argument: >vim - echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) + echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) " 1 at width 2 is: 01 < However, you can't use it as a different type: >vim - echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) + echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) " E1502: Positional argument 2 used as field width reused as " different type: long int/int < *E1503* When a positional argument is used, but not the correct number or arguments is given, an error is raised: >vim - echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) + echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) " E1503: Positional argument 3 out of bounds: %1$d at width - " %2$d is: %01$*2$.*3$d + " %2$d is: %1$0*2$.*3$d < Only the first error is reported: >vim - echo printf("%01$*2$.*3$d %4$d", 1, 2) - " E1503: Positional argument 3 out of bounds: %01$*2$.*3$d + echo printf("%1$0*2$.*3$d %4$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d " %4$d < *E1504* diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 653989ec41..1a13885ceb 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -6744,24 +6744,24 @@ function vim.fn.prevnonblank(lnum) end --- < --- *E1502* --- You can re-use a [field-width] (or [precision]) argument: >vim ---- echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) +--- echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) --- " 1 at width 2 is: 01 --- < --- However, you can't use it as a different type: >vim ---- echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) +--- echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) --- " E1502: Positional argument 2 used as field width reused as --- " different type: long int/int --- < --- *E1503* --- When a positional argument is used, but not the correct number --- or arguments is given, an error is raised: >vim ---- echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) +--- echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) --- " E1503: Positional argument 3 out of bounds: %1$d at width ---- " %2$d is: %01$*2$.*3$d +--- " %2$d is: %1$0*2$.*3$d --- < --- Only the first error is reported: >vim ---- echo printf("%01$*2$.*3$d %4$d", 1, 2) ---- " E1503: Positional argument 3 out of bounds: %01$*2$.*3$d +--- echo printf("%1$0*2$.*3$d %4$d", 1, 2) +--- " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d --- " %4$d --- < --- *E1504* diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5b5fcd214b..73367ee86b 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -8191,24 +8191,24 @@ M.funcs = { < *E1502* You can re-use a [field-width] (or [precision]) argument: >vim - echo printf("%1$d at width %2$d is: %01$*2$d", 1, 2) + echo printf("%1$d at width %2$d is: %1$0*2$d", 1, 2) " 1 at width 2 is: 01 < However, you can't use it as a different type: >vim - echo printf("%1$d at width %2$ld is: %01$*2$d", 1, 2) + echo printf("%1$d at width %2$ld is: %1$0*2$d", 1, 2) " E1502: Positional argument 2 used as field width reused as " different type: long int/int < *E1503* When a positional argument is used, but not the correct number or arguments is given, an error is raised: >vim - echo printf("%1$d at width %2$d is: %01$*2$.*3$d", 1, 2) + echo printf("%1$d at width %2$d is: %1$0*2$.*3$d", 1, 2) " E1503: Positional argument 3 out of bounds: %1$d at width - " %2$d is: %01$*2$.*3$d + " %2$d is: %1$0*2$.*3$d < Only the first error is reported: >vim - echo printf("%01$*2$.*3$d %4$d", 1, 2) - " E1503: Positional argument 3 out of bounds: %01$*2$.*3$d + echo printf("%1$0*2$.*3$d %4$d", 1, 2) + " E1503: Positional argument 3 out of bounds: %1$0*2$.*3$d " %4$d < *E1504* diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0538ee2b22..31d4a2ecfd 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -968,6 +968,11 @@ static char *format_typename(const char *type) static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const char *type) FUNC_ATTR_NONNULL_ALL { + if (arg <= 0) { + semsg(_(e_invalid_format_specifier_str), type); + return FAIL; + } + if (*ap_types == NULL || *num_posarg < arg) { const char **new_types = *ap_types == NULL ? xcalloc((size_t)arg, sizeof(const char *)) diff --git a/test/old/testdir/test_format.vim b/test/old/testdir/test_format.vim index c2126f583c..585f152029 100644 --- a/test/old/testdir/test_format.vim +++ b/test/old/testdir/test_format.vim @@ -300,6 +300,8 @@ func Test_printf_pos_errors() call CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%1$*1$.*0$s')"], "E1505:") + call CheckLegacyAndVim9Failure(["call printf('%*0$s')"], "E1505:") call CheckLegacyAndVim9Failure(["call printf('%.123456789$d', 5)"], "E1510:") call CheckLegacyAndVim9Failure(["call printf('%.123456789d', 5)"], "E1510:")