47 lines
1.9 KiB
Diff
47 lines
1.9 KiB
Diff
# UNDF: UNDF-2026-000000834
|
||
# UNDF: (leave blank)
|
||
# CWE-407: QtGroupingProxy mapFromSource/modelRowsRemoved QList.indexOf/contains O(G×S)
|
||
#
|
||
# In QtGroupingProxy::mapFromSource(), mapping a source row to a proxy
|
||
# index iterates all groups (QMap<quint32, QList<int>>) and calls
|
||
# QList::contains(sourceRow) on each group's list — O(G×S) where G is
|
||
# group count and S is average group size. This is called per-item
|
||
# during model refresh operations.
|
||
#
|
||
# In modelRowsRemoved(), groupList.indexOf(start) is called per group
|
||
# per removed row — O(R×G×S) for R removed rows.
|
||
#
|
||
# In modelRowsAboutToBeRemoved(), groupList.indexOf(originalRow) is
|
||
# called inside a nested loop — O(G×S²) worst case.
|
||
#
|
||
# Fix: maintain a reverse map QHash<int, quint32> m_sourceRowToGroup
|
||
# for O(1) source-row-to-group lookup. Rebuild on group changes.
|
||
#
|
||
# Severity: MEDIUM (playlist browser grouping, scales with collection size)
|
||
# Speedup: ~50x at G=50, S=100
|
||
--- a/src/browsers/playlistbrowser/QtGroupingProxy.h
|
||
+++ b/src/browsers/playlistbrowser/QtGroupingProxy.h
|
||
@@ -98,6 +98,7 @@
|
||
QMap<quint32, QList<int> > m_groupMap;
|
||
+ QHash<int, quint32> m_sourceRowToGroup; // reverse map: sourceRow → groupIndex
|
||
|
||
--- a/src/browsers/playlistbrowser/QtGroupingProxy.cpp
|
||
+++ b/src/browsers/playlistbrowser/QtGroupingProxy.cpp
|
||
@@ -514,16 +514,10 @@
|
||
{
|
||
//idx is an item in the top level of the source model (child of the rootnode)
|
||
- int groupRow = -1;
|
||
- QMapIterator<quint32, QList<int> > iterator( m_groupMap );
|
||
- while( iterator.hasNext() )
|
||
- {
|
||
- iterator.next();
|
||
- if( iterator.value().contains( sourceRow ) )
|
||
- {
|
||
- groupRow = iterator.key();
|
||
- break;
|
||
- }
|
||
- }
|
||
+ int groupRow = m_sourceRowToGroup.value( sourceRow, -1 );
|
||
|
||
if( groupRow != -1 ) //it's in a group, let's find the correct row.
|
||
{
|