2.9 KiB
ejabberd — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
Two O(n²) defects in ejabberd's message archiving and shared roster modules. Both use lists:member/2 in hot paths — one on every archived message, the other on every group membership status subscription. Measured at 250× and 2,500× respectively. Patches ready for upstream review.
The Defects
ejabberd-0001 (PATCHED — HIGH): src/mod_mam.erl:1029
%% Inside message archiving hot path — per archived message:
lists:member(LPeer, Always) orelse lists:member(LPeer, Never)
%% O(N×M) — N messages × M always/never list entries
lists:member/2 performs O(N) linear scan over the Always/Never lists on every archived message. For N messages and M filter list entries: O(N × M). Measured ratio: 250×.
ejabberd-0002 (PATCHED — HIGH): mod_shared_roster.erl:356
%% Inside is_user_in_group() — per subscription stanza:
lists:member(User, GroupMembers)
%% O(N_group×msg) — group size × messages
lists:member/2 scan over group members on every shared roster subscription stanza. Measured ratio: 2,500×.
Complexity Proof
ejabberd-0001: For M=250 always/never filter entries:
- Per message: O(M) scan
- Fixed: pre-built
gb_setsorsets→ O(log M) or O(1) - 250× measured ratio.
ejabberd-0002: For group size G=2500:
- Per stanza: O(G) scan
- Fixed:
gb_setsset → O(log G) - 2,500× measured ratio.
Impact
ejabberd-0001 affects all ejabberd servers with mod_mam (Message Archive Management) enabled — the XMPP standard for message history. High-traffic XMPP servers with large always/never filter lists hit worst case on every message.
ejabberd-0002 affects all ejabberd servers with mod_shared_roster enabled — a widely-used ejabberd module that automatically populates contact lists from group membership. Large XMPP deployments (corporate XMPP servers, FOSS communities) use shared roster extensively.
The Fix
ejabberd-0001: Replace lists:member with gb_sets:is_member:
%% Before
lists:member(LPeer, Always) orelse lists:member(LPeer, Never)
%% After
%% CWE-407 fix: gb_sets for O(log N) membership instead of O(N) lists:member scan.
gb_sets:is_member(LPeer, AlwaysSet) orelse gb_sets:is_member(LPeer, NeverSet)
ejabberd-0002: Pre-build gb_sets:from_list(GroupMembers) before the subscription loop.
Patch
defects/ejabberd/patch/ejabberd-0001-0002-mam-roster-sets.patch
What We Ask
- Confirm receipt and assign a GitHub Security Advisory or issue reference.
- Validate the patch against your mod_mam and mod_shared_roster test suites.
- Assess CVE eligibility — ejabberd-0002 measured at 2,500× on large group rosters.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.