Closed Bug 2081101 Opened 1 month ago Closed 17 days ago

Quadratic address-group accumulation in JSMime causes mail-triggered UI denial of service

Categories

(MailNews Core :: MIME, defect)

defect

Tracking

(thunderbird_esr153 wontfix, thunderbird160 wontfix)

RESOLVED FIXED
161 Branch
Tracking Status
thunderbird_esr153 --- wontfix
thunderbird160 --- wontfix

People

(Reporter: tjddbdud1, Assigned: mkmelin)

References

()

Details

(Keywords: csectype-dos, reporter-external, sec-low, Whiteboard: [client-bounty-form][deduped])

Attachments

(1 file, 1 external file)

Summary

Thunderbird's JSMime address parser repeatedly replaces the accumulated result array with results.concat(addrlist) while recovering from malformed address-group separators. Each call copies every address accumulated so far. A compact header such as To: a,:a,:a,:... therefore causes O(n²) element copying.

Address headers are parsed synchronously on the main thread while Thunderbird renders a message. An attacker who can deliver a crafted message can make the Thunderbird UI unresponsive when the victim previews or opens it. The attached 451,846-byte EML stalled the tested official Daily build for approximately 15 seconds. Larger headers extend the stall further.

The sample contains no RFC 2047 encoded-word markers. This is a separate root cause from Bug 2081099: the issue is repeated immutable array concatenation, and it also reproduces when RFC 2047 decoding is disabled.

Security impact

  • Remote attacker-controlled input: an ordinary folded To, Cc, From, or other address header.
  • Required user action: preview or open the crafted message.
  • Result: main-thread CPU and allocation pressure causing a temporary application-wide UI denial of service.
  • Confidentiality/integrity impact: none observed.
  • Suggested CWE: CWE-407 (Inefficient Algorithmic Complexity); secondary CWE-400 (Uncontrolled Resource Consumption).
  • Final security rating is left to Mozilla's triage team, as requested by the Client Bug Bounty reporting guidance.

Affected code

Current comm-central revision tested: 4d8f5f89a54280d9e7df4fa692dc29e59add9b73.

  1. mailnews/mime/jsmime/jsmime.mjs:888-1130

parseAddressingHeader() builds a temporary addrlist and a top-level results array.

  1. mailnews/mime/jsmime/jsmime.mjs:982-991

On each colon, pending entries are appended with:

if (addrlist.length > 0) {
  results = results.concat(addrlist);
}
addrlist = [];

The three-byte pattern a,: first adds one entry to addrlist, then takes this branch. For n repetitions, the parser copies result arrays of lengths 1, 2, ..., n, for n(n+1)/2 total element copies.

  1. mailnews/mime/jsmime/jsmime.mjs:1060-1076

The malformed-semicolon recovery path contains the same pattern. Repeating a,; triggers equivalent quadratic behavior.

  1. mailnews/mime/src/MimeJSComponents.sys.mjs:262-268

MailServices.headerParser.parseEncodedHeaderW() invokes the affected address parser.

  1. mail/base/content/msgHdrView.js:1595-1603

outputEmailAddresses() calls parseEncodedHeaderW() synchronously while rendering message headers.

Supported versions checked

The two vulnerable results.concat(addrlist) sites and the UI call path are present in the official source tags for:

  • Thunderbird 157.0.1 release (THUNDERBIRD_157_0_1_RELEASE, commit ac1c50734e8df36f2ca56cd4dbe96558ec469b15)
  • Thunderbird 153.4.0 ESR (THUNDERBIRD_153_4_0esr_RELEASE, commit 39e4af053e7a49fb2feb892ea77810f6280e5341)
  • Thunderbird 140.17.0 ESR build 2 (THUNDERBIRD_140_17_0esr_BUILD2, commit 060fdd85f49aa9a992a0a6023aa883afe1d92177)
  • Thunderbird Daily 160.0a1 build 20261012100649, dynamically reproduced

Reproduction

  1. Use a disposable Thunderbird profile.
  2. Open poc-452kb.eml using File > Open > Saved Message. The same rendering path is used when previewing a message in a folder.
  3. Observe that Thunderbird's main UI is unresponsive for roughly 15 seconds while the To header is parsed and rendered.
  4. Open the same-size control-452kb.eml. It completes substantially faster.

The PoC is inert text. It does not use scripts, external resources, credentials, or network requests. generate_poc.mjs regenerates both samples.

Measurements

Environment:

  • Windows 11 Pro 64-bit, version 10.0.26200
  • Official Thunderbird Daily 160.0a1, build ID 20261012100649
  • Isolated test profile

End-to-end EML open time:

Input EML size Open time
Candidate, 20,000 repetitions 60,545 B 439.5 ms
Candidate, 40,000 repetitions 120,746 B 1,381.4 ms
Candidate, 80,000 repetitions 241,145 B 4,067.1 ms
Same-size linear control, 100,000 repetitions 301,344 B 627.1 ms
Candidate, 100,000 repetitions 301,348 B 6,475.3 ms
Candidate, 150,000 repetitions 451,846 B 15,005.4 ms

Direct timing of MailServices.headerParser.parseEncodedHeaderW():

Header bytes Control Candidate
24,000 14.0 ms 48.1 ms
48,000 28.8 ms 177.3 ms
96,000 56.5 ms 604.3 ms
192,000 174.0 ms 1,349.7 ms
300,000 199.2 ms 2,908.1 ms

The same-size 300 KB parser inputs produce the same 100,000 result entries, but the candidate is approximately 14.6 times slower. End-to-end, the 301 KB candidate is approximately 10.3 times slower than the same-size control.

Root cause and suggested fix

Append the temporary entries without replacing and copying the full result array. A loop avoids both quadratic copies and the argument-count limits of a large spread call:

for (const item of addrlist) {
  results.push(item);
}

Both results.concat(addrlist) sites should be changed. A regression test can parse many short malformed groups and verify that the result is correct without repeatedly replacing the result array.

Public duplicate search

  • Bug 1411720 / CVE-2018-5161 concerned exponential escaping of repeated @ characters and was fixed for Thunderbird 60. This input does not exercise that encoding path.
  • Bug 1055077 concerns general quick-filter performance across many messages, not repeated copying within one address header.
  • Bug 1059988 concerns compatibility with semicolon-separated addresses, not algorithmic complexity or UI denial of service.
  • Bug 2081099 is my pending report about unanchored RFC 2047 regular-expression searches in the tokenizer. This report contains no =? markers and exercises different lines and a different fix.

I did not find a public report for the current results.concat(addrlist) quadratic path.

PoC integrity

  • poc-452kb.eml SHA-256: D4778C041876B302F7431AA3DF00B6B407765F23065DD6BE38B3AEF8D1C245FC
  • control-452kb.eml SHA-256: 222F092CC7B962ED085A5B26104BB91AFE488EEC3451FDF5FFAA669C034B0314
Flags: sec-bounty?
Whiteboard: [client-bounty-form] → [client-bounty-form][deduped]

Automatic triage (under development): sec-rating
No validation or verification has been performed.

Security rating: sec-low

Confirmed in current comm-central: parseAddressingHeader() rebuilds the accumulated result array with results.concat(addrlist) on every ':' token and in the ';' recovery path, so a header shaped like 'To: a,:a,:a,:...' makes the parser copy 1+2+...+n entries, i.e. O(n^2) work for a linear-size input. The parser is reachable directly from remote message content on the main thread with no privileged position required: message header display (outputEmailAddresses -> parseEncodedHeaderW), the thread pane sender/recipient columns (nsMsgDBView::FetchAuthor/FetchRecipients -> EncodedHeader -> ParseEncodedHeader) and global-search indexing (GlodaUtils.parseMailAddresses) all parse the stored From/To/Cc values, so receiving and viewing a crafted message freezes the whole application UI for an attacker-chosen duration. The impact is limited to availability - no memory corruption, no information disclosure, peak memory stays bounded, and the hang clears itself once parsing finishes - which places it at the low end of denial-of-service issues rather than a severe or persistent DoS.

Group: firefox-core-security → mail-core-security
Component: Security → MIME
Product: Firefox → MailNews Core

Final security rating is left to Mozilla's triage team, as requested by the Client Bug Bounty reporting guidance.

I'm sorry, the Client Bug Bounty only covers Firefox, not Thunderbird.

I think we can open this up.

Assignee: nobody → mkmelin+mozilla
Group: mail-core-security
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
See Also: → 2083198
Duplicate of this bug: 2083229
Target Milestone: --- → 161 Branch

Pushed by daniel@thunderbird.net:
https://hg.mozilla.org/comm-central/rev/54f570011080
Avoid repeated array copies when collecting addresses in jsmime. r=tobyp

Status: ASSIGNED → RESOLVED
Closed: 17 days ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: