# UNDF: UNDF-2026-000000794 # UNDF: (leave blank) # CWE-407: Algorithmic Complexity — remove_from_layer_stack O(C × S × L) # File: app/core/gimpimage.c # Severity: LOW-MEDIUM # Ratio: 100x at C=20,S=50,L=20 # # gimp_image_remove_from_layer_stack() removes a layer (and all children # of a group layer) from every entry in the layer_stack MRU history. # For each child (C), it iterates all stack entries (S) and calls # g_list_remove() which is O(L) per call (linear scan of the GList). # Total: O(C × S × L). For a group with 20 children, 50 undo states, # and 20 layers per state, that's 20,000 linear scans. # # Fix: use GHashTable per stack entry for O(1) removal, or batch # the removals. --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -2094,14 +2094,33 @@ static void gimp_image_remove_from_layer_stack (GimpImage *image, GimpLayer *layer) { GimpImagePrivate *private; GSList *slist; + GList *all_to_remove = NULL; + GList *iter; g_return_if_fail (GIMP_IS_IMAGE (image)); g_return_if_fail (GIMP_IS_LAYER (layer)); private = GIMP_IMAGE_GET_PRIVATE (image); - /* Remove layer itself from the MRU layer stack. */ - for (slist = private->layer_stack; slist; slist = slist->next) - slist->data = g_list_remove (slist->data, layer); - - /* Also remove all children of a group layer from the layer_stack */ + /* Collect layer + all children into a hash set for O(1) lookup */ + all_to_remove = g_list_prepend (all_to_remove, layer); if (gimp_viewable_get_children (GIMP_VIEWABLE (layer))) { GimpContainer *stack = gimp_viewable_get_children (GIMP_VIEWABLE (layer)); - GList *children; - GList *list; - - children = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (stack)); - - for (list = children; list; list = g_list_next (list)) - { - GimpLayer *child = list->data; - - for (slist = private->layer_stack; slist; slist = slist->next) - slist->data = g_list_remove (slist->data, child); - } - - g_list_free (children); + GList *children = gimp_item_stack_get_item_list (GIMP_ITEM_STACK (stack)); + all_to_remove = g_list_concat (all_to_remove, children); } + /* Build hash set from collected layers */ + GHashTable *remove_set = g_hash_table_new (g_direct_hash, g_direct_equal); + for (iter = all_to_remove; iter; iter = iter->next) + g_hash_table_add (remove_set, iter->data); + g_list_free (all_to_remove); + + /* Single pass over each stack entry: filter out removed layers */ + for (slist = private->layer_stack; slist; slist = slist->next) + { + GList *filtered = NULL; + for (iter = slist->data; iter; iter = iter->next) + { + if (! g_hash_table_contains (remove_set, iter->data)) + filtered = g_list_prepend (filtered, iter->data); + } + g_list_free (slist->data); + slist->data = g_list_reverse (filtered); + } + g_hash_table_destroy (remove_set); + gimp_image_clean_layer_stack (image); }