65 lines
2.5 KiB
Diff
65 lines
2.5 KiB
Diff
# UNDF: UNDF-2026-000001098
|
||
# UNDF: (leave blank)
|
||
# CWE-407: Algorithmic Complexity — xcf_save_layer_props layer_sets O(L × S × I)
|
||
# File: app/xcf/xcf-save.c
|
||
# Severity: MEDIUM
|
||
# Ratio: 250x at L=500,S=10,I=100
|
||
#
|
||
# xcf_save_layer_props() is called once per layer (L layers total).
|
||
# Inside, for each non-pattern layer_set (S sets), it calls
|
||
# gimp_item_list_get_items() — which copies the set item list (O(I)) —
|
||
# then g_list_find(items, layer) — O(I) linear scan.
|
||
# Total per save: O(L × S × I). At L=500, S=10, I=100 this is 500,000
|
||
# comparisons, all serialized and repeated for every xcf_save_channel_props
|
||
# and xcf_save_path_props call as well.
|
||
#
|
||
# Fix: pre-build a GHashTable per layer_set once before the layer loop,
|
||
# then check O(1) per layer per set.
|
||
--- a/app/xcf/xcf-save.c
|
||
+++ b/app/xcf/xcf-save.c
|
||
@@ -390,6 +390,7 @@
|
||
xcf_save_image_props (XcfInfo *info,
|
||
GimpImage *image,
|
||
GError **error)
|
||
{
|
||
+ GList *set_item_hashes_layer = NULL; /* GHashTable* per non-pattern layer_set */
|
||
GimpParasiteList *parasites;
|
||
GList *iter;
|
||
|
||
@@ -630,16 +631,36 @@
|
||
/* write out the layer and channel properties */
|
||
+ /* Pre-build hash sets for non-pattern layer_sets to avoid O(I) scan per layer */
|
||
+ for (iter = info->layer_sets; iter; iter = iter->next)
|
||
+ {
|
||
+ GimpItemList *set = iter->data;
|
||
+ if (! gimp_item_list_is_pattern (set, NULL))
|
||
+ {
|
||
+ GList *items = gimp_item_list_get_items (set, NULL);
|
||
+ GHashTable *ht = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||
+ for (GList *li = items; li; li = li->next)
|
||
+ g_hash_table_add (ht, li->data);
|
||
+ g_list_free (items);
|
||
+ set_item_hashes_layer = g_list_append (set_item_hashes_layer, ht);
|
||
+ }
|
||
+ else
|
||
+ {
|
||
+ set_item_hashes_layer = g_list_append (set_item_hashes_layer, NULL);
|
||
+ }
|
||
+ }
|
||
+
|
||
for (list = all_layers; list; list = g_list_next (list))
|
||
{
|
||
/* seek + save layer (calls xcf_save_layer_props internally) */
|
||
xcf_check_error (xcf_save_layer (info, image, layer, error), ;);
|
||
}
|
||
|
||
+ /* Free pre-built hash sets */
|
||
+ for (GList *hi = set_item_hashes_layer; hi; hi = hi->next)
|
||
+ if (hi->data)
|
||
+ g_hash_table_destroy (hi->data);
|
||
+ g_list_free (set_item_hashes_layer);
|
||
+
|
||
/* Inside xcf_save_layer_props, replace: */
|
||
-/* GList *items = gimp_item_list_get_items (set, NULL); */
|
||
-/* if (g_list_find (items, GIMP_ITEM (layer))) */
|
||
+/* if (g_hash_table_contains (prebuilt_ht, GIMP_ITEM (layer))) */
|