java-topology/defects/scribus/patch/scribus-0001-sorted-style-list-contains.patch
russell@unturf.com 13db08af50 darktable+scribus: CWE-407 scan — 6 defects (3 darktable, 3 scribus)
darktable-0001: map_locations image diff g_list_find O(N*M) MEDIUM 375x
darktable-0002: tags _tag_add_tags_to_list g_list_find O(T*L) MEDIUM 375x
darktable-0003: map view clustering g_list_find(sel_imgs) O(I²×S) HIGH 160x
scribus-0001: getSortedStyleList retList.contains O(N²) MEDIUM 167x (×4 copies)
scribus-0002: getUsedPatterns results.contains O(I×R) MEDIUM 98x
scribus-0003: Selection::addItems m_SelList.contains O(N×M) MEDIUM 2100x
2026-03-30 11:28:25 -04:00

75 lines
2.3 KiB
Diff

# UNDF: UNDF-2026-000000802
# UNDF: (leave blank)
# scribus-0001: getSortedStyleList retList.contains O(N²) dedup
#
# In scribus/scribusdoc.cpp, four identical functions — getSortedStyleList(),
# getSortedCharStyleList(), getSortedTableStyleList(), getSortedCellStyleList()
# — walk the style parent chain and accumulate indices into retList using
# QList<int>::contains() to dedup. QList::contains is O(N) per call, and the
# outer loop is O(N) over all styles, making overall complexity O(N²).
#
# Additionally, the inner while-loop walking up the parent chain accumulates
# into retList2 with retList2.contains(pp) — a minor O(depth²) per style.
#
# For a document with N=500 paragraph styles (plausible in long-form publishing
# with inherited house styles), this is ~125,000 linear scans instead of ~500
# hash lookups.
#
# Fix: maintain a companion QSet<int> for O(1) membership test alongside the
# ordered QList<int> retList.
#
# Severity: MEDIUM — 4 identical defect sites; triggered on style
# reorder/display; scales with style count.
--- a/scribus/scribusdoc.cpp
+++ b/scribus/scribusdoc.cpp
@@ -1198,6 +1198,7 @@
QList<int> ScribusDoc::getSortedStyleList() const
{
QList<int> retList;
+ QSet<int> retSet;
for (int i = 0; i < m_docParagraphStyles.count(); ++i)
{
if (m_docParagraphStyles[i].parent().isEmpty())
{
- if (!retList.contains(i))
+ if (!retSet.contains(i))
+ {
retList.append(i);
+ retSet.insert(i);
+ }
continue;
}
QList<int> retList2;
+ QSet<int> retSet2;
...
retList2.prepend(i);
+ retSet2.insert(i);
while ((!par.isEmpty()) && (par != name))
{
int pp = m_docParagraphStyles.find(par);
- if ((pp >= 0) && (!retList2.contains(pp)))
+ if ((pp >= 0) && (!retSet2.contains(pp)))
+ {
retList2.prepend(pp);
+ retSet2.insert(pp);
+ }
par = (pp >= 0) ? m_docParagraphStyles[pp].parent() : QString();
}
for (int r = 0; r < retList2.count(); ++r)
{
- if (!retList.contains(retList2[r]))
+ if (!retSet.contains(retList2[r]))
+ {
retList.append(retList2[r]);
+ retSet.insert(retList2[r]);
+ }
}
}
return retList;
}
// Same fix applies identically to:
// getSortedCharStyleList() (line 1230)
// getSortedTableStyleList() (line 1262)
// getSortedCellStyleList() (line 1294)