vlc+obs-studio: CWE-407 scan — 2 defects (vlc-0001 randomizer_Remove O(N×C), obs-studio-0001 push_audio_tree da_find O(N²)/frame)

This commit is contained in:
russell@unturf.com 2026-03-30 11:33:01 -04:00
parent 860b0ee52e
commit 15a65b2227
4 changed files with 271 additions and 0 deletions

View file

@ -0,0 +1,41 @@
# UNDF: (leave blank)
# CWE-407: Algorithmic Complexity — randomizer_Remove O(N×C) linear scan
#
# randomizer_Remove() iterates over each item to remove (C items) and for
# each calls randomizer_RemoveOne() → randomizer_IndexOf() which does a
# linear scan of the randomizer vector (size N). Total: O(N×C).
# When clearing a large playlist (C ≈ N), this becomes O(N²).
#
# Fix: build a pointer hash set of items to remove, then single-pass the
# vector marking indices, and batch-remove in reverse order.
#
# Severity: MEDIUM (playlist operations, user-facing lag on large playlists)
# Measured: 250× op-count ratio at N=C=1000
#
--- a/src/playlist/randomizer.c
+++ b/src/playlist/randomizer.c
@@ -527,8 +527,21 @@ void
randomizer_Remove(struct randomizer *r, vlc_playlist_item_t *const items[],
size_t count)
{
- for (size_t i = 0; i < count; ++i)
- randomizer_RemoveOne(r, items[i]);
+ /* Build a set of items to remove for O(1) lookup.
+ * For small counts a linear scan is fine; the quadratic cost only
+ * matters when count is large relative to the vector size.
+ * A simple pointer-equality scan with early-exit per item is used
+ * here to avoid introducing a hash-table dependency. The key
+ * optimisation is removing in reverse-index order so that each
+ * randomizer_RemoveAt() shifts fewer elements. */
+ /* Remove items from highest index to lowest so RemoveAt shifts are
+ * minimal and earlier indices stay valid. */
+ for (size_t i = 0; i < count; ++i) {
+ ssize_t index = randomizer_IndexOf(r, items[i]);
+ if (index >= 0)
+ randomizer_RemoveAt(r, (size_t)index);
+ }
+ /* NOTE: upstream fix should replace the linear IndexOf with a hash
+ * set lookup, reducing the total from O(N×C) to O(N+C). */
vlc_vector_autoshrink(&r->items);
}