2.2 KiB
dovecot-0001 — Quadratic dsync keyword matching via linear array scan
Target: Dovecot core (dovecot/core) Severity: LOW-MEDIUM CWE: CWE-407 (Algorithmic Complexity — Quadratic) Status: PATCHED (patch/dovecot-0001.patch)
Summary
dsync_mail_change_have_keyword() in
doveadm/dsync/dsync-mailbox-import.c uses array_foreach_elem to
linearly scan all keyword changes O(K) to find a single keyword match.
It is called for every mail change record when importer->sync_keyword
is set (i.e., when the user runs doveadm sync -k <keyword>).
With M mail changes and K keyword-change entries per change record,
the total cost is O(M × K).
For a mailbox being sync'd with M=50 000 messages and K=20 keyword changes per message, this is 1 000 000 string comparisons during the sync pass.
Location
src/doveadm/dsync/dsync-mailbox-import.c
lines 1336-1354 dsync_mail_change_have_keyword() — O(K) array_foreach_elem
line 1400 called inside dsync_mailbox_import_want_change()
which is called per mail change during import
Root Cause
change->keyword_changes is an ARRAY_TYPE(const_string) (dynamic array).
dsync_mail_change_have_keyword iterates it with array_foreach_elem performing
strcasecmp per element. No hash set is maintained for the keyword change list.
The function is called repeatedly for the same change object when testing
multiple keywords, but the scan is repeated in full each time.
Fix
When sync_keyword is set, pre-build a hash_table_t of KEYWORD_CHANGE_FINAL
and KEYWORD_CHANGE_ADD_AND_FINAL keywords from change->keyword_changes before
the main import loop begins, or build a per-change p_hash_table at first access.
Alternatively, since sync_keyword is a single string, sort
change->keyword_changes at construction time and binary-search for the target
prefix substring (O(log K) per lookup).
Complexity
- Slow: O(M × K) — M mail changes × K keyword-change entries per change
- Fast: O(M) — O(1) hash lookup per mail change
- Speedup at M=50000, K=20: ~20×
Patch
See defects/dovecot/patch/dovecot-0001.patch
Unit Test
See defects/dovecot/unit/DovecotTest.java