]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0196: textprop: negative IDs and can cause a crash v9.2.0196
authorHirohito Higashi <h.east.727@gmail.com>
Wed, 18 Mar 2026 21:03:17 +0000 (21:03 +0000)
committerChristian Brabandt <cb@256bit.org>
Wed, 18 Mar 2026 21:11:41 +0000 (21:11 +0000)
Problem:  textprop: negative IDs and can cause a crash without "text"
          (Paul Ollis)
Solution: Strictly reserve negative IDs for virtual text, ignore "id"
          when "text" is provided in prop_add() (Hirohito Higashi).

When prop_add() was called with a negative id and no "text", the
property was stored with a negative tp_id.  A subsequent call to
prop_list() or screen redraw would then treat it as a virtual text
property and dereference b_textprop_text.ga_data, which is NULL when
no virtual text properties exist.

Negative ids are reserved for virtual text properties, so always
reject them with E1293 regardless of whether virtual text properties
exist.  Also, when "text" is specified any user-provided id is now
silently ignored and an internal negative id is assigned.

Remove the now-unnecessary did_use_negative_pop_id flag and E1339.
Update E1293's message and the documentation accordingly.

related: #19684
closes:  #19741

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/tags
runtime/doc/textprop.txt
src/errors.h
src/po/vim.pot
src/testdir/test_textprop.vim
src/textprop.c
src/version.c

index 5117c07efcb3678779c96a2394b6014f38f9b713..77b19ec7ffe126f8e0bcd7ebba69d1a4ef2ba3a7 100644 (file)
@@ -4573,7 +4573,6 @@ E1335     vim9class.txt   /*E1335*
 E1336  options.txt     /*E1336*
 E1337  vim9class.txt   /*E1337*
 E1338  vim9class.txt   /*E1338*
-E1339  textprop.txt    /*E1339*
 E134   change.txt      /*E134*
 E1340  vim9class.txt   /*E1340*
 E1341  vim9class.txt   /*E1341*
index 3fa7d14a518992accf51b3135650b3ec5c983420..fbe4231c7fd4f175c982ee2b20d83371f6cd01fd 100644 (file)
@@ -141,9 +141,9 @@ prop_add({lnum}, {col}, {props})
                                the current buffer is used
                   id           user defined ID for the property; must be a
                                number, should be positive |E1510|;
-                               when using "text" then "id" must not be
-                               present and will be set automatically to a
-                               negative number; otherwise zero is used
+                               when using "text" then any "id" value is
+                               ignored and a negative number is assigned
+                               automatically; otherwise zero is used
                                                        *E1305*
                   text         text to be displayed before {col}, or
                                above/below the line if {col} is zero; prepend
@@ -224,14 +224,9 @@ prop_add({lnum}, {col}, {props})
                is difficult to compute).
                A negative "id" will be chosen and is returned.
 
-               Before text properties with text were supported it was
-               possible to use a negative "id", even though this was very
-               rare.  Now that negative "id"s are reserved for text
-               properties with text an error is given when using a negative
-               "id".  When a text property with text already exists using a
-               negative "id" results in *E1293* .  If a negative "id" was
-               used and later a text property with text is added results in
-               *E1339* .
+               Negative "id"s are reserved for text properties with "text"
+               and cannot be used otherwise.  Using a negative "id" results
+               in *E1293* .
 
                Can also be used as a |method|: >
                        GetLnum()->prop_add(col, props)
index 9c4e485ef86ab3743c4d641f6ac623f5d3fe3a60..d780fb4207ea6db66555b7209adabb702eb3782d 100644 (file)
@@ -3328,8 +3328,8 @@ EXTERN char e_invalid_argument_nr[]
 EXTERN char e_cmdline_window_already_open[]
        INIT(= N_("E1292: Command-line window is already open"));
 #ifdef FEAT_PROP_POPUP
-EXTERN char e_cannot_use_negative_id_after_adding_textprop_with_text[]
-       INIT(= N_("E1293: Cannot use a negative id after adding a textprop with text"));
+EXTERN char e_cannot_use_negative_id[]
+       INIT(= N_("E1293: Cannot use a negative id for a text property"));
 EXTERN char e_can_only_use_text_align_when_column_is_zero[]
        INIT(= N_("E1294: Can only use text_align when column is zero"));
 #endif
@@ -3432,10 +3432,7 @@ EXTERN char e_class_variable_str_not_found_in_class_str[]
        INIT(= N_("E1337: Class variable \"%s\" not found in class \"%s\""));
 // E1338 unused
 #endif
-#ifdef FEAT_PROP_POPUP
-EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
-       INIT(= N_("E1339: Cannot add a textprop with text after using a textprop with a negative id"));
-#endif
+// E1339 unused
 #ifdef FEAT_EVAL
 EXTERN char e_argument_already_declared_in_class_str[]
        INIT(= N_("E1340: Argument already declared in the class: %s"));
index 446a0b91f59227f0b72f4b5072da0c27d39f299f..d67bcafcc998e32c68ba85a6e37fd0f4b9800ae7 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Vim\n"
 "Report-Msgid-Bugs-To: vim-dev@vim.org\n"
-"POT-Creation-Date: 2026-03-13 18:26+0000\n"
+"POT-Creation-Date: 2026-03-18 21:08+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -8099,7 +8099,7 @@ msgstr ""
 msgid "E1292: Command-line window is already open"
 msgstr ""
 
-msgid "E1293: Cannot use a negative id after adding a textprop with text"
+msgid "E1293: Cannot use a negative id for a text property"
 msgstr ""
 
 msgid "E1294: Can only use text_align when column is zero"
@@ -8243,11 +8243,6 @@ msgstr ""
 msgid "E1337: Class variable \"%s\" not found in class \"%s\""
 msgstr ""
 
-msgid ""
-"E1339: Cannot add a textprop with text after using a textprop with a "
-"negative id"
-msgstr ""
-
 #, c-format
 msgid "E1340: Argument already declared in the class: %s"
 msgstr ""
index 975a5846ac4e24d306bbd80ba9bd80159aac0648..f8323019bd5e21b1db4218ba5717609737801537 100644 (file)
@@ -4669,47 +4669,18 @@ endfunc
 
 func Test_error_when_using_negative_id()
   call prop_type_add('test1', #{highlight: 'ErrorMsg'})
-  call prop_add(1, 1, #{type: 'test1', text: 'virtual'})
-  call assert_fails("call prop_add(1, 1, #{type: 'test1', length: 1, id: -1})", 'E1293:')
-
-  call prop_type_delete('test1')
-endfunc
-
-func Test_error_after_using_negative_id()
-  CheckScreendump
-  " This needs to run a separate Vim instance because the
-  " "did_use_negative_pop_id" will be set.
-  CheckRunVimInTerminal
-
-  let lines =<< trim END
-      vim9script
 
-      setline(1, ['one', 'two', 'three'])
-      prop_type_add('test_1', {highlight: 'Error'})
-      prop_type_add('test_2', {highlight: 'WildMenu'})
-
-      prop_add(3, 1, {
-          type: 'test_1',
-          length: 5,
-          id: -1
-      })
-
-      def g:AddTextprop()
-          prop_add(1, 0, {
-              type: 'test_2',
-              text: 'The quick fox',
-              text_padding_left: 2
-          })
-      enddef
-  END
-  call writefile(lines, 'XtextPropError', 'D')
-  let buf = RunVimInTerminal('-S XtextPropError', #{rows: 8, cols: 60})
-  call VerifyScreenDump(buf, 'Test_prop_negative_error_1', {})
+  " Negative id is always rejected.  Before the fix, prop_add() with a negative
+  " id succeeded when no virtual text existed, then prop_list() would dereference
+  " a NULL pointer (b_textprop_text.ga_data) and crash.
+  call assert_fails("call prop_add(1, 1, #{type: 'test1', length: 1, id: -1})", 'E1293:')
+  call assert_equal([], prop_list(1))
 
-  call term_sendkeys(buf, ":call AddTextprop()\<CR>")
-  call VerifyScreenDump(buf, 'Test_prop_negative_error_2', {})
+  " id is silently ignored when text is also specified.
+  let propid = prop_add(1, 1, #{type: 'test1', text: 'virtual', id: 42})
+  call assert_true(propid < 0)
 
-  call StopVimInTerminal(buf)
+  call prop_type_delete('test1')
 endfunc
 
 func Test_modify_text_before_prop()
index c2519e993da1e388475ac60aea28ba22fe14a133..f0d418eee2f3f2c349a5457b6eca2540376d63c8 100644 (file)
@@ -434,10 +434,6 @@ get_textprop_id(buf_T *buf)
     return -(buf->b_textprop_text.ga_len + 1);
 }
 
-// Flag that is set when a negative ID isused for a normal text property.
-// It is then impossible to use virtual text properties.
-static int did_use_negative_pop_id = FALSE;
-
 /*
  * Shared between prop_add() and popup_create().
  * "dict_arg" is the function argument of a dict containing "bufnr".
@@ -599,24 +595,13 @@ prop_add_common(
     if (dict_arg != NULL && get_bufnr_from_arg(dict_arg, &buf) == FAIL)
        goto theend;
 
-    if (id < 0)
-    {
-       if (buf->b_textprop_text.ga_len > 0)
-       {
-           emsg(_(e_cannot_use_negative_id_after_adding_textprop_with_text));
-           goto theend;
-       }
-       did_use_negative_pop_id = TRUE;
-    }
-
     if (text != NULL)
-    {
-       if (did_use_negative_pop_id)
-       {
-           emsg(_(e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id));
-           goto theend;
-       }
+       // Always assign an internal negative id; ignore any user-provided id.
        id = get_textprop_id(buf);
+    else if (id < 0)
+    {
+       emsg(_(e_cannot_use_negative_id));
+       goto theend;
     }
 
     // This must be done _before_ we add the property because property changes
@@ -981,7 +966,11 @@ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf)
 {
     proptype_T *pt;
     int buflocal = TRUE;
-    int virtualtext_prop = prop->tp_id < 0;
+    // A negative tp_id normally means a virtual text property, but a user
+    // may set a negative id for a regular property when no virtual text
+    // properties exist.  Guard against that by checking the index is valid.
+    int virtualtext_prop = prop->tp_id < 0
+                          && -prop->tp_id - 1 < buf->b_textprop_text.ga_len;
 
     dict_add_number(dict, "col", (prop->tp_col == MAXCOL) ? 0 : prop->tp_col);
     if (!virtualtext_prop)
index 4a23daa867a9cfd8547823a881d00b7700312450..73c466b41e176a8ed881a72376fd6ba786cd407f 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    196,
 /**/
     195,
 /**/