From a72b71498cdb2e459f6b0217e36f54405c8da4d2 Mon Sep 17 00:00:00 2001 From: Pier Angelo Vendrame <pierov@torproject.org> Date: Tue, 26 Mar 2024 17:39:01 +0100 Subject: [PATCH] Bug 41901: Hardcode normalized FontSubstitutes. Windows has a system to set font aliases through the registry. This allows some customization that could be used as a fingerprinting vector. Moreover, this mechanism is used by Windows itself, and different SKUs might have different default FontSubstitutes. --- gfx/thebes/StandardFonts-win10.inc | 65 +++++++++++++++++++++++++ gfx/thebes/gfxDWriteFontList.cpp | 77 +++++++++++++++++++----------- gfx/thebes/gfxDWriteFontList.h | 3 ++ gfx/thebes/gfxGDIFontList.cpp | 28 +++++++++++ 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/gfx/thebes/StandardFonts-win10.inc b/gfx/thebes/StandardFonts-win10.inc index c929992ed865f..81cf30cc376f3 100644 --- a/gfx/thebes/StandardFonts-win10.inc +++ b/gfx/thebes/StandardFonts-win10.inc @@ -200,3 +200,68 @@ static const char* kLangPackFonts[] = { // "Rockwell Nova", // Pan-European Supplemental Fonts - EXCLUDED // "Verdana Pro", // Pan-European Supplemental Fonts - EXCLUDED }; + +struct FontSubstitute { + const char *substituteName; + const char *actualFontName; +}; + +static const FontSubstitute kFontSubstitutes[] = { + // Common substitutions + {"Arabic Transparent", "Arial"}, + {"Arabic Transparent Bold", "Arial Bold"}, + {"Arial Baltic", "Arial"}, + {"Arial CE", "Arial"}, + {"Arial CYR", "Arial"}, + {"Arial Greek", "Arial"}, + {"Arial TUR", "Arial"}, + {"Courier New Baltic", "Courier New"}, + {"Courier New CE", "Courier New"}, + {"Courier New CYR", "Courier New"}, + {"Courier New Greek", "Courier New"}, + {"Courier New TUR", "Courier New"}, + {"Helv", "MS Sans Serif"}, + {"Helvetica", "Arial"}, + {"MS Shell Dlg 2", "Tahoma"}, + {"Tahoma Armenian", "Tahoma"}, + {"Times", "Times New Roman"}, + {"Times New Roman Baltic", "Times New Roman"}, + {"Times New Roman CE", "Times New Roman"}, + {"Times New Roman CYR", "Times New Roman"}, + {"Times New Roman Greek", "Times New Roman"}, + {"Times New Roman TUR", "Times New Roman"}, + {"Tms Rmn", "MS Serif"}, + // Common, except Japanese (which uses MS UI Gothic, instead) + {"MS Shell Dlg", "Microsoft Sans Serif"}, + // Arabic + {"Arial (Arabic)", "Arial"}, + {"Courier New (Arabic)", "Courier New"}, + {"Times New Roman (Arabic)", "Times New Roman"}, + // Cyrillic + Greek + {"Courier", "Courier New"}, + // Greek + {"Fixedsys Greek", "Fixedsys"}, + {"MS Serif Greek", "MS Serif"}, + {"MS Sans Serif Greek", "MS Sans Serif"}, + {"Small Fonts Greek", "Small Fonts"}, + {"System Greek", "System"}, + // Hebrew + {"Arial (Hebrew)", "Arial"}, + {"Courier New (Hebrew)", "Courier New"}, + {"David Transparent", "David"}, + {"Fixed Miriam Transparent", "Miriam Fixed"}, + {"Miriam Transparent", "Miriam"}, + {"Rod Transparent", "Rod"}, + {"Times New Roman (Hebrew)", "Times New Roman"}, + // Japanese + {"標準明æœ", "ï¼ï¼³ 明æœ"}, + {"標準ゴシック", "ï¼ï¼³ ゴシック"}, + {"ゴシック", "ï¼ï¼³ ゴシック"}, + {"ゴシック", "ï¼ï¼³ ゴシック"}, + {"クーリエ", "Courier"}, + {"タイムズロï¾ï¾", "Times New Roman"}, + {"ï¾ï¾™ï¾ï¾žï¾ï½¶", "Arial"}, + // Simplified Chinese + {"FangSong_GB2312", "FangSong"}, + {"KaiTi_GB2312", "KaiTi"}, +}; diff --git a/gfx/thebes/gfxDWriteFontList.cpp b/gfx/thebes/gfxDWriteFontList.cpp index 8595004d21002..f06fba22133f6 100644 --- a/gfx/thebes/gfxDWriteFontList.cpp +++ b/gfx/thebes/gfxDWriteFontList.cpp @@ -1953,6 +1953,20 @@ static void RemoveCharsetFromFontSubstitute(nsACString& aName) { #define MAX_VALUE_DATA 512 nsresult gfxDWriteFontList::GetFontSubstitutes() { + if (nsContentUtils::ShouldResistFingerprinting( + "Ignore any fingerprintable user font customization and normalize " + "font substitutes across different Windows SKUs.", + RFPTarget::FontVisibilityLangPack)) { + for (const FontSubstitute& fs : kFontSubstitutes) { + nsAutoCString substituteName(fs.substituteName); + nsAutoCString actualFontName(fs.actualFontName); + BuildKeyNameFromFontName(substituteName); + BuildKeyNameFromFontName(actualFontName); + AddSubstitute(substituteName, actualFontName); + } + return NS_OK; + } + HKEY hKey; DWORD i, rv, lenAlias, lenActual, valueType; WCHAR aliasName[MAX_VALUE_NAME]; @@ -1987,39 +2001,46 @@ nsresult gfxDWriteFontList::GetFontSubstitutes() { BuildKeyNameFromFontName(substituteName); RemoveCharsetFromFontSubstitute(actualFontName); BuildKeyNameFromFontName(actualFontName); - if (SharedFontList()) { - // Skip substitution if the original font is available, unless the option - // to apply substitutions unconditionally is enabled. - if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) { - // Font substitutions are recorded for the canonical family names; we - // don't need FindFamily to consider localized aliases when searching. - if (SharedFontList()->FindFamily(substituteName, - /*aPrimaryNameOnly*/ true)) { - continue; - } - } - if (SharedFontList()->FindFamily(actualFontName, + AddSubstitute(substituteName, actualFontName); + } + + return NS_OK; +} + +void gfxDWriteFontList::AddSubstitute(const nsCString& substituteName, + const nsCString& actualFontName) { + if (SharedFontList()) { + // Skip substitution if the original font is available, unless the + // option to apply substitutions unconditionally is enabled. + if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) { + // Font substitutions are recorded for the canonical family names; + // we don't need FindFamily to consider localized aliases when + // searching. + if (SharedFontList()->FindFamily(substituteName, /*aPrimaryNameOnly*/ true)) { - mSubstitutions.InsertOrUpdate(substituteName, - MakeUnique<nsCString>(actualFontName)); - } else if (mSubstitutions.Get(actualFontName)) { - mSubstitutions.InsertOrUpdate( - substituteName, - MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName))); - } else { - mNonExistingFonts.AppendElement(substituteName); + return; } + } + if (SharedFontList()->FindFamily(actualFontName, + /*aPrimaryNameOnly*/ true)) { + mSubstitutions.InsertOrUpdate(substituteName, + MakeUnique<nsCString>(actualFontName)); + } else if (mSubstitutions.Get(actualFontName)) { + mSubstitutions.InsertOrUpdate( + substituteName, + MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName))); } else { - gfxFontFamily* ff; - if (!actualFontName.IsEmpty() && - (ff = mFontFamilies.GetWeak(actualFontName))) { - mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff}); - } else { - mNonExistingFonts.AppendElement(substituteName); - } + mNonExistingFonts.AppendElement(substituteName); + } + } else { + gfxFontFamily* ff; + if (!actualFontName.IsEmpty() && + (ff = mFontFamilies.GetWeak(actualFontName))) { + mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff}); + } else { + mNonExistingFonts.AppendElement(substituteName); } } - return NS_OK; } struct FontSubstitution { diff --git a/gfx/thebes/gfxDWriteFontList.h b/gfx/thebes/gfxDWriteFontList.h index 357336a12cc05..d416c0a8d5349 100644 --- a/gfx/thebes/gfxDWriteFontList.h +++ b/gfx/thebes/gfxDWriteFontList.h @@ -457,6 +457,9 @@ class gfxDWriteFontList final : public gfxPlatformFontList { const nsTArray<nsCString>* aForceClassicFams = nullptr) MOZ_REQUIRES(mLock); + void AddSubstitute(const nsCString& substituteName, + const nsCString& actualFontName); + #ifdef MOZ_BUNDLED_FONTS already_AddRefed<IDWriteFontCollection> CreateBundledFontsCollection( IDWriteFactory* aFactory); diff --git a/gfx/thebes/gfxGDIFontList.cpp b/gfx/thebes/gfxGDIFontList.cpp index 4dca4506c0ac6..a3257d540d839 100644 --- a/gfx/thebes/gfxGDIFontList.cpp +++ b/gfx/thebes/gfxGDIFontList.cpp @@ -31,6 +31,10 @@ #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/Telemetry.h" +#include "nsContentUtils.h" + +#include "StandardFonts-win10.inc" + #include <usp10.h> using namespace mozilla; @@ -50,6 +54,10 @@ static __inline void BuildKeyNameFromFontName(nsAString& aName) { if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1); ToLowerCase(aName); } +static __inline void BuildKeyNameFromFontName(nsACString& aName) { + if (aName.Length() >= LF_FACESIZE) aName.Truncate(LF_FACESIZE - 1); + ToLowerCase(aName); +} // Implementation of gfxPlatformFontList for Win32 GDI, // using GDI font enumeration APIs to get the list of fonts @@ -529,6 +537,26 @@ static void RemoveCharsetFromFontSubstitute(nsAString& aName) { #define MAX_VALUE_DATA 512 nsresult gfxGDIFontList::GetFontSubstitutes() { + if (nsContentUtils::ShouldResistFingerprinting( + "Ignore any fingerprintable user font customization and normalize " + "font substitutes across different Windows SKUs.", + RFPTarget::FontVisibilityLangPack)) { + for (const FontSubstitute& fs : kFontSubstitutes) { + nsAutoCString substituteName(fs.substituteName); + nsAutoCString actualFontName(fs.actualFontName); + BuildKeyNameFromFontName(substituteName); + BuildKeyNameFromFontName(actualFontName); + gfxFontFamily* ff; + if (!actualFontName.IsEmpty() && + (ff = mFontFamilies.GetWeak(actualFontName))) { + mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff}); + } else { + mNonExistingFonts.AppendElement(substituteName); + } + } + return NS_OK; + } + HKEY hKey; DWORD i, rv, lenAlias, lenActual, valueType; WCHAR aliasName[MAX_VALUE_NAME]; -- GitLab