java-topology/whitepaper/outreach/ejabberd.md

2.9 KiB
Raw Blame History

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_sets or sets → O(log M) or O(1)
  • 250× measured ratio.

ejabberd-0002: For group size G=2500:

  • Per stanza: O(G) scan
  • Fixed: gb_sets set → 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

  1. Confirm receipt and assign a GitHub Security Advisory or issue reference.
  2. Validate the patch against your mod_mam and mod_shared_roster test suites.
  3. Assess CVE eligibility — ejabberd-0002 measured at 2,500× on large group rosters.
  4. Coordinate a disclosure date — we are targeting 90 days from first contact.

Contact: see cover email. This brief is confidential until coordinated disclosure.