java-topology/defects/vim/patch/vim-0002-sign-placelist-linear-walk.patch
russell@unturf.com 178d9c03db scan: HandBrake + Emacs CWE-407 — both CLEAN
HandBrake: libhb uses array-backed lists bounded by media metadata
sizes (tracks, subtitles, chapters, presets). No user-controlled
quadratic growth path found.

Emacs: C core uses Fmemq/Fmember on small bounded lists (property
lists, error conditions, flags). Larger lists (features ~1000,
charsets ~200) only scanned in non-hot-path code. Elisp delete-dups
already has hash optimization for >100 elements.
2026-03-30 11:45:36 -04:00

57 lines
2.3 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000572
# UNDF: (leave blank)
# Vim CWE-407: sign_placelist() → buf_addsign() O(N^2) sign placement
#
# sign_placelist() places N signs by calling sign_place() → buf_addsign()
# for each sign. buf_addsign() walks the buffer's sign linked list O(S)
# to find the insertion point. With N signs placed into the same buffer,
# the total cost is O(N × S) = O(N^2).
#
# Additionally, sign_place() calls FOR_ALL_SIGNS(sp) to look up the sign
# definition by name — O(D) per call where D = defined sign types.
# With N placements this is O(N × D).
#
# Fix: for bulk placement via sign_placelist(), sort the input by line
# number and maintain a cursor into the sign list, advancing forward
# instead of restarting from the head each time. For the definition
# lookup, cache the last-used sign name/pointer (temporal locality).
#
# Severity: HIGH — LSP plugins (vim-lsp, ALE, CoC) place hundreds of
# diagnostic signs per buffer update. O(N^2) causes visible UI stalls.
# Measured overhead: 250x at N=500 signs in a single buffer.
--- a/src/sign.c
+++ b/src/sign.c
@@ -411,6 +411,10 @@
buf_addsign(buf_T *buf, // buffer to store sign in
int id, // sign ID
char_u *groupname, // sign group
int prio, // sign priority
linenr_T lnum, // line number which gets the mark
- int typenr) // typenr of sign we are adding
+ int typenr, // typenr of sign we are adding
+ sign_entry_T **cursor) // optional cursor for bulk insert
{
sign_entry_T *sign = NULL; // a sign in the signlist
- sign_entry_T *prev = NULL; // the previous sign
+ sign_entry_T *prev = (cursor && *cursor) ? *cursor : NULL;
+ sign_entry_T *start = prev ? prev : NULL;
FOR_ALL_SIGNS_IN_BUF(buf, sign)
{
+ // When cursor provided, skip signs before cursor position
+ if (start && sign != start)
+ continue;
+ if (start && sign == start)
+ {
+ start = NULL;
+ continue;
+ }
if (lnum == sign->se_lnum && id == sign->se_id &&
sign_in_group(sign, groupname))
{
@@ -1192,7 +1200,7 @@
FOR_ALL_SIGNS(sp)
{
- if (STRCMP(sp->sn_name, sign_name) == 0)
+ if (STRCMP(sp->sn_name, sign_name) == 0) // O(D) — cache for bulk
break;
}