# UNDF: UNDF-2026-000000094 --- a/gst/gstelementfactory.c +++ b/gst/gstelementfactory.c @@ -1183,6 +1183,73 @@ gst_element_factory_list_filter (GList * list, const GstCaps * caps, GstPadDirection direction, gboolean subsetonly) { GQueue results = G_QUEUE_INIT; + GHashTable *caps_index; + GList *indexed_candidates; + + /* + * CWE-407 fix: build a temporary media-type → factory hash for this filter + * call. The outer list (already pre-filtered by type via list_get_elements) + * is typically 50–500 entries. Without indexing, every entry requires + * gst_caps_can_intersect() which itself is O(S1 × S2). + * + * Strategy: extract the media type (first structure name) from each factory's + * pad templates during a single O(N) pass to build a hash. Then look up only + * the query caps media type. Factories with ANY/empty caps are kept in a + * fallback list examined after the hash hits. + * + * This reduces the inner loop from O(N × M × S²) to O(k × M × S²) where + * k is the number of factories that handle the specific media type (≈1–5). + */ + + if (!list || !caps || GST_CAPS_IS_ANY (caps) || GST_CAPS_IS_EMPTY (caps)) + goto slow_path; + + caps_index = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + (GDestroyNotify) g_list_free); + + { + GList *fallback = NULL; + for (GList *l = list; l; l = l->next) { + GstElementFactory *factory = (GstElementFactory *) l->data; + const GList *templates = + gst_element_factory_get_static_pad_templates (factory); + gboolean has_any = FALSE; + for (const GList *t = templates; t; t = g_list_next (t)) { + GstStaticPadTemplate *tmpl = t->data; + if (tmpl->direction != direction) continue; + const gchar *tmpl_str = tmpl->static_caps.string; + if (!tmpl_str || g_str_has_prefix (tmpl_str, "ANY") || + g_str_has_prefix (tmpl_str, "EMPTY")) { + has_any = TRUE; + continue; + } + /* Extract media type: everything up to first ',' or ' ' */ + const gchar *comma = strpbrk (tmpl_str, ", "); + gchar *mtype = comma + ? g_strndup (tmpl_str, (gsize)(comma - tmpl_str)) + : g_strdup (tmpl_str); + GList *bucket = g_hash_table_lookup (caps_index, mtype); + g_hash_table_insert (caps_index, mtype, + g_list_prepend (bucket, factory)); + } + if (has_any) + fallback = g_list_prepend (fallback, factory); + } + /* Merge hash hits + fallback into candidate list */ + indexed_candidates = g_list_copy (fallback); + g_list_free (fallback); + for (guint i = 0; i < gst_caps_get_size (caps); i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + const gchar *mtype = gst_structure_get_name (s); + GList *bucket = g_hash_table_lookup (caps_index, mtype); + for (GList *b = bucket; b; b = b->next) + if (!g_list_find (indexed_candidates, b->data)) + indexed_candidates = g_list_prepend (indexed_candidates, b->data); + } + g_hash_table_destroy (caps_index); + /* Run the actual caps check only on the candidate set */ + list = indexed_candidates; + } + +slow_path: GST_DEBUG ("finding factories"); /* loop over all the factories */ @@ -1224,6 +1291,12 @@ gst_element_factory_list_filter (GList * list, } } } + + if (list == indexed_candidates) + g_list_free (indexed_candidates); + return results.head; }