83 lines
3 KiB
Diff
83 lines
3 KiB
Diff
# 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);
|
||
}
|