Address Linux voice-list fingerprint surface
Categories
(Core :: Privacy: Anti-Tracking, 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-dispatcheris 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 havespeech-dispatcherrunning. 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-dataversion the distro ships. Different espeak-ng releases expose different base-voice and variant counts, producing distinctvoices_sha1values.
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
-
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. -
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
181ad3db74a0cohort that does not appear at any meaningful share in non-Arch-family distros. - Debian has a unique
5b48772d2c53cohort 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 theundefined(populate-timeout) cohort: 28.74% vs ≤2% elsewhere — flatpak's async voice-population path is particularly slow.
- Fedora is overwhelmingly dominated by one hash (~90% on
-
Per-distro classification is achievable from a single observed
voices_sha1in many cases — e.g., a client reporting5b48772d2c53is almost certainly Debian,181ad3db74a0is almost certainly Arch-family,undefinedis over-represented on flatpak. -
Two unidentified hashes warrant inspection:
5b48772d2c53(Debian, 33.66%) and181ad3db74a0(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).
Description
•