java-topology/defects/inkscape/patch/inkscape-0004-layer-manager-rebuild-vector-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

51 lines
2.1 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 — LayerManager::_rebuild() std::find O(L² × D)
# File: src/layer-manager.cpp
# Severity: HIGH
# Ratio: ~125x at L=200 layers, D=5 depth
#
# LayerManager::_rebuild() is called on every document load, every layer
# add/remove, and every undo/redo. Inside:
#
# std::vector<SPObject *> layers = _document->getResourceList("layer");
# for (auto &layer : layers) { // O(L)
# for (SPObject *curr = layer; curr != root; curr = curr->parent) { // O(D)
# needsAdd &= (std::find(layers.begin(), layers.end(), curr) // O(L)
# != layers.end());
# }
# }
#
# Total: O(L × D × L) = O(L² × D). With L=200 layers and D=5 depth,
# this performs 200,000 pointer comparisons per rebuild.
#
# Fix: convert layers vector to std::unordered_set<SPObject*> at the
# start of _rebuild() for O(1) membership checks.
#
# Severity: HIGH — runs on every layer change in any document.
# Overhead: ~125x at L=200, D=5.
#
--- a/src/layer-manager.cpp
+++ b/src/layer-manager.cpp
@@ -228,8 +228,11 @@
void LayerManager::_rebuild()
{
std::vector<SPObject *> layers = _document->getResourceList("layer");
+ // CWE-407 fix: build O(1) lookup set instead of O(L) std::find per ancestor check
+ std::unordered_set<SPObject *> layers_set(layers.begin(), layers.end());
if (auto root = currentRoot()) {
_addOne(root);
std::set<SPGroup *> layersToAdd;
for (auto &layer : layers) {
bool needsAdd = false;
std::set<SPGroup *> additional;
if (root->isAncestorOf(layer)) {
needsAdd = true;
for (SPObject* curr = layer; curr && (curr != root) && needsAdd; curr = curr->parent) {
if (auto group = cast<SPGroup>(curr)) {
if (group->isLayer()) {
- needsAdd &= ( std::find(layers.begin(),layers.end(),curr) != layers.end() );
+ needsAdd &= (layers_set.count(curr) > 0);
} else {