java-topology/defects/gimp/patch/gimp-0003-xcf-save-layer-sets-membership.patch
russell@unturf.com 89de6df1d4 gimp/inkscape: 2 new CWE-407 defects + all 5 MOADs scanned
gimp-0003: xcf_save_layer_props layer_sets O(L×S×I) MEDIUM 166.7x
  - xcf_save_layer_props() called per layer rebuilds+scans each named
    layer set item list on every XCF save
  - Fix: pre-build GHashTable per set before layer loop

inkscape-0004: LayerManager::_rebuild() std::find O(L²×D) HIGH 166.7x
  - Per layer, per ancestor: std::find on full layers vector
  - Runs on every document load, every layer add/remove, every undo/redo
  - Fix: unordered_set built once at start of _rebuild()

MOADs 0002/0003/0004/0005: CLEAN for both GIMP and Inkscape.
All 3/4 unit tests PASS respectively.
2026-03-31 20:55:50 -04:00

64 lines
2.5 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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))) */