patch 9.2.0196: textprop: negative IDs and can cause a crash

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>
This commit is contained in:
Hirohito Higashi
2026-03-18 21:03:17 +00:00
committed by Christian Brabandt
parent 2b70de167e
commit 0c109e4e60
7 changed files with 32 additions and 84 deletions
-1
View 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*
+6 -11
View 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)
+3 -6
View 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"));
+2 -7
View 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 ""
+9 -38
View File
@@ -4669,49 +4669,20 @@ endfunc
func Test_error_when_using_negative_id()
call prop_type_add('test1', #{highlight: 'ErrorMsg'})
call prop_add(1, 1, #{type: 'test1', text: 'virtual'})
" 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))
" 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 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', {})
call term_sendkeys(buf, ":call AddTextprop()\<CR>")
call VerifyScreenDump(buf, 'Test_prop_negative_error_2', {})
call StopVimInTerminal(buf)
endfunc
func Test_modify_text_before_prop()
CheckScreendump
CheckRunVimInTerminal
+10 -21
View 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)
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
196,
/**/
195,
/**/