Open Bug 2043374 Opened 4 months ago Updated 3 months ago

Address Linux voice-list fingerprint surface

Categories

(Core :: Privacy: Anti-Tracking, enhancement)

enhancement

Tracking

()

People

(Reporter: tjr, Unassigned)

References

(Blocks 1 open bug)

Details

Summary

speechSynthesis.getVoices() exposes the user's full installed-voice list on Linux to the web — a list whose exact contents classify the user's distro, package state, and installation method (snap / flatpak / native / arch-family) to a measurable degree.

Background

Firefox on Linux sources its voice list through dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp:356-390, calling spd_list_synthesis_voices() against a running speech-dispatcher daemon. The daemon enumerates voices from its configured modules — typically espeak-ng-mbrola-generic. The total voice count is base_voices × variants (e.g., 14805 = 141 × 105 for espeak-ng 1.52.0). When speech-dispatcher is not running or not reachable, the voice list is empty. Two structural axes therefore drive the per-cohort variation:

  • Whether speech-dispatcher is installed and running on the host. Firefox's own ability to talk to a host speechd has been resolved across the major packaging methods — snap via socket activation (Bug 1729750, FIXED) and flatpak via host-socket access (Bug 1638150, FIXED in Firefox 123). The remaining empty-voice cohort therefore reflects hosts that simply do not have speech-dispatcher running. Empirically, Arch-family installs (Arch / Cachyos / Manjaro) overwhelmingly don't have it (82-90% report no voices), while Ubuntu / Mint / Fedora installs mostly do (12-20% no voices). The exact mechanism is some mix of "not pulled in by the user's chosen desktop environment", "not autostarted by default", and "user didn't install it manually" — the empirical fact is what's robust, the specific packaging cause is less clear-cut. Firefox 116 added a user-visible notification for the speechd-missing case via Bug 1432719, but this did not change the underlying voice-list behavior.
  • Which espeak-ng-data version the distro ships. Different espeak-ng releases expose different base-voice and variant counts, producing distinct voices_sha1 values.

Distribution data

All Linux

voices_sha1 (prefix) % of cohort identity
e68620c9c500 50.70% voices_count=13362 (espeak-ng 1.51, modern Ubuntu/Mint/Fedora)
da39a3ee5e6b 24.24% voices_count=0 (SHA1 of empty — speechd unreachable)
0547ac17c1b4 14.52% voices_count=8611 (espeak-ng 1.50.x, older Ubuntu LTS)
9e0343035024 6.52% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
undefined 1.75% voices_count=0 (populate-timeout / async race; Linux O6)
(779 more distinct values) 2.26% each < 1% of cohort

Ubuntu-core

Snap-packaged Ubuntu Firefox.

voices_sha1 (prefix) % of cohort identity
e68620c9c500 56.68% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 22.38% voices_count=8611 (espeak-ng 1.50.x)
da39a3ee5e6b 12.11% voices_count=0 (speechd unreachable)
9e0343035024 8.60% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
(318 more distinct values) 0.22% each < 1% of cohort

Ubuntu (native, non-snap)

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 40.24% voices_count=0 (speechd unreachable)
e68620c9c500 29.33% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 16.37% voices_count=8611 (espeak-ng 1.50.x)
9e0343035024 4.85% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
12d0e3cd1453 2.85% (unidentified)
2e69bb699267 2.72% (unidentified)
21be849d466d 2.04% (unidentified)
(105 more distinct values) 1.59% each < 1% of cohort

Linuxmint

voices_sha1 (prefix) % of cohort identity
e68620c9c500 72.71% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 12.36% voices_count=8611 (espeak-ng 1.50.x)
da39a3ee5e6b 12.20% voices_count=0 (speechd unreachable)
9e0343035024 1.80% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
(183 more distinct values) 0.93% each < 1% of cohort

Fedora

voices_sha1 (prefix) % of cohort identity
e68620c9c500 89.67% voices_count=13362 (espeak-ng 1.51)
da39a3ee5e6b 5.56% voices_count=0 (speechd unreachable)
9e0343035024 4.20% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
(41 more distinct values) 0.57% each < 1% of cohort

Org.freedesktop.platform

The flatpak runtime (i.e., flatpak Firefox).

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 42.35% voices_count=0 (speechd unreachable on host — most flatpak users don't have speech-dispatcher installed/running)
undefined 28.74% voices_count=0 (populate-timeout / async race)
e68620c9c500 18.75% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 5.12% voices_count=8611 (espeak-ng 1.50.x)
9e0343035024 4.49% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
(49 more distinct values) 0.55% each < 1% of cohort

Arch

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 81.97% voices_count=0 (speechd unreachable — most Arch installs do not have speech-dispatcher running)
9e0343035024 14.70% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
181ad3db74a0 2.77% (unidentified — Arch-family-specific cohort)
(192 more distinct values) 0.57% each < 1% of cohort

Cachyos

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 89.83% voices_count=0 (speechd unreachable)
9e0343035024 7.52% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
181ad3db74a0 2.49% (unidentified — Arch-family-specific cohort)
(52 more distinct values) 0.16% each < 1% of cohort

Debian

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 44.38% voices_count=0 (speechd unreachable)
5b48772d2c53 33.66% (unidentified — Debian-specific cohort, likely voices_count=1500)
9e0343035024 11.43% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
e68620c9c500 7.56% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 1.22% voices_count=8611 (espeak-ng 1.50.x)
(53 more distinct values) 1.75% each < 1% of cohort

Pop!_OS

A clean Ubuntu derivative.

voices_sha1 (prefix) % of cohort identity
e68620c9c500 62.00% voices_count=13362 (espeak-ng 1.51)
0547ac17c1b4 34.97% voices_count=8611 (espeak-ng 1.50.x)
da39a3ee5e6b 2.79% voices_count=0 (speechd unreachable)
(38 more distinct values) 0.23% each < 1% of cohort

Manjaro

voices_sha1 (prefix) % of cohort identity
da39a3ee5e6b 89.32% voices_count=0 (speechd unreachable — Arch-family base)
181ad3db74a0 8.14% (unidentified — Arch-family-specific cohort)
9e0343035024 2.08% voices_count=14805 (espeak-ng 1.52.0 + mbrola)
(67 more distinct values) 0.46% each < 1% of cohort

Observations from the data

  1. The five identified top-level hashes (e68620c9c500, da39a3ee5e6b, 0547ac17c1b4, 9e0343035024, undefined) together cover ~97.7% of Linux clients, and they map cleanly onto a few (espeak-ng-data version, speechd reachable?) combinations.

  2. The distribution-of-hashes is highly distro-dependent, even though the underlying input (the getVoices() web API) is identical. A small per-distro snapshot:

    • Fedora is overwhelmingly dominated by one hash (~90% on e68620c9c500) — narrowest distribution.
    • Pop!_OS has essentially two cohorts (62% and 35%) — a clean Ubuntu-derivative split.
    • Linuxmint and snap-Ubuntu are dominated by the same four hashes that span "all Linux".
    • Arch, Cachyos, and Manjaro are dominated by the empty-voice hash (~82-90%), with a distinct Arch-family 181ad3db74a0 cohort that does not appear at any meaningful share in non-Arch-family distros.
    • Debian has a unique 5b48772d2c53 cohort at 33.66% that does not appear at meaningful share in any other distro.
    • Native Ubuntu (non-snap) has a much longer tail than snap-Ubuntu — likely reflecting more diverse user-installed packages on the host.
    • Flatpak (Org.freedesktop.platform) has the largest share of the undefined (populate-timeout) cohort: 28.74% vs ≤2% elsewhere — flatpak's async voice-population path is particularly slow.
  3. Per-distro classification is achievable from a single observed voices_sha1 in many cases — e.g., a client reporting 5b48772d2c53 is almost certainly Debian, 181ad3db74a0 is almost certainly Arch-family, undefined is over-represented on flatpak.

  4. Two unidentified hashes warrant inspection: 5b48772d2c53 (Debian, 33.66%) and 181ad3db74a0 (Arch-family, 2-8% across Arch / Cachyos / Manjaro). These almost certainly correspond to specific (espeak-ng-data version, speechd module configuration) combinations on those distros

Possible remediations

We did not collect the whole list of installed voices, so we don't know the overlap between espeak-ng versions, or what causes the long tail of users. Like fonts. we could ship an allowlist and only allow voices through on the allowlist, which we could engineer to collapse the difference between versions. It would not help the users in the long tail that perhaps have a large subset of the allowlist, but not every item.

The above assumes we do a local data gathering effort comparing espeak-ng versions. We could also do it via telemetry to understand the situation on the long tail users.

We could also change the API - have it return no voices before user interaction, restrict it in third party iframes. (Both of which are weak defenses to be sure).

You need to log in before you can comment on or make changes to this bug.