diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index fd089aa9e5..9876936d5d 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1122,19 +1122,22 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // so any user-visible characters must occur before here. char *out_end_p = (out + outlen) - 1; +#define MAY_GROW_STL_ITEMS_LEN() \ + do { \ + if (curitem == (int)stl_items_len) { \ + size_t new_len = stl_items_len * 3 / 2; \ + stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len); \ + stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len); \ + stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1)); \ + stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1)); \ + stl_items_len = new_len; \ + } \ + } while (0) + // Proceed character by character through the statusline format string // fmt_p is the current position in the input buffer for (char *fmt_p = usefmt; *fmt_p != NUL;) { - if (curitem == (int)stl_items_len) { - size_t new_len = stl_items_len * 3 / 2; - - stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len); - stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len); - stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1)); - stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1)); - - stl_items_len = new_len; - } + MAY_GROW_STL_ITEMS_LEN(); if (*fmt_p != '%') { prevchar_isflag = prevchar_isitem = false; @@ -1559,6 +1562,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op if (!left_align_num) { stl_items[curitem].type = Separate; stl_items[curitem++].start = out_p; + MAY_GROW_STL_ITEMS_LEN(); } } else if (stcp == NULL) { num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0 : wp->w_cursor.lnum; @@ -1709,6 +1713,7 @@ stcsign: } } stl_items[curitem++].type = fdc > 0 ? HighlightFold : HighlightSign; + MAY_GROW_STL_ITEMS_LEN(); } str = buf_tmp; break; @@ -1800,6 +1805,7 @@ stcsign: // If we made it this far, the item is normal and starts at // our current position in the output buffer. // Non-normal items would have `continued`. + assert(curitem < (int)stl_items_len); stl_items[curitem].start = out_p; stl_items[curitem].type = Normal; @@ -1984,10 +1990,12 @@ stcsign: curitem++; // For a 'statuscolumn' number item that is left aligned, add a separator item. if (left_align_num) { + MAY_GROW_STL_ITEMS_LEN(); stl_items[curitem].type = Separate; stl_items[curitem++].start = out_p; } } +#undef MAY_GROW_STL_ITEMS_LEN *out_p = NUL; diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index 34e3a68477..2f5d9107a8 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -1243,4 +1243,47 @@ describe('statuscolumn', function() | ]]) end) + + describe('no heap-buffer-overflow with', function() + it('%s after many items #41630', function() + exec([[ + set nonumber signcolumn=yes + let &statuscolumn = repeat('%=', 99) .. '%s' + ]]) + screen:expect([[ + {7: }aaaaa |*4 + {7: }^aaaaa | + {7: }aaaaa |*8 + | + ]]) + assert_alive() + end) + + it('%l after many items', function() + exec([[ + func Statuscolumn() + return repeat('%=', v:relnum == 0 ? 149 : 99) .. '%l ' + endfunc + set number numberwidth=4 relativenumber + set statuscolumn=%!Statuscolumn() + ]]) + screen:expect([[ + {8: 4 }aaaaa | + {8: 3 }aaaaa | + {8: 2 }aaaaa | + {8: 1 }aaaaa | + {8:8 }^aaaaa | + {8: 1 }aaaaa | + {8: 2 }aaaaa | + {8: 3 }aaaaa | + {8: 4 }aaaaa | + {8: 5 }aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | + {8: 8 }aaaaa | + | + ]]) + assert_alive() + end) + end) end)