Firefox火狐指纹浏览器定制WebGPU指纹方案说明

Firefox火狐指纹浏览器定制WebGPU指纹方案说明

本文档说明当前 Firefox 代码里 WebGPU 指纹方案的文件落点、改动内容和改动原因。这里记录的是 Firefox 版实现,不是之前 WebKit 方案的迁移说明。

目标

目标是让页面侧看到一组可配置、互相一致的 WebGPU 能力,而不是直接暴露真实机器或因为系统 WebGPU 不可用而显示不支持。

BrowserScan 一类站点通常会检查这些点:

  • navigator.gpu 是否存在。
  • canvas.getContext("webgpu") 是否可用。
  • navigator.gpu.requestAdapter() 是否返回 adapter。
  • adapter.infovendorarchitecturedevicedescriptionsubgroupMinSizesubgroupMaxSizeisFallbackAdapter
  • adapter.featuresdevice.features
  • adapter.limitsdevice.limits
  • device.adapterInfo 是否和 adapter.info 一致。
  • navigator.gpu.wgslLanguageFeatures
  • navigator.gpu.getPreferredCanvasFormat()

因此改动不是只把一个开关打开,而是让 WebGPU 的暴露、adapter、device、features、limits、WGSL 和 canvas format 走同一份 fp.txt 配置。

配置入口

运行 Firefox 时需要带 --fpfile,例如当前方案使用:

txt 复制代码
C:\firefox\fp.txt

WebGPU 相关字段采用两种命名兼容:

  • 推荐新格式:webgpu.enabled:truewebgpu.vendor:AMDwebgpu.limits.maxTextureDimension2D:16384
  • 兼容旧格式:webgpu_enabled=1webgpu_vendor=AMDwebgpu_max_texture_dimension_2d=16384

当前 fp.txt 中主要字段示例:

txt 复制代码
webgpu.enabled:true
webgpu.vendor:AMD
webgpu.architecture:rdna2
webgpu.device:AMD Radeon RX 6800 XT
webgpu.description:ANGLE (AMD, AMD Radeon RX 6800 XT Direct3D12)
webgpu.subgroupMinSize:4
webgpu.subgroupMaxSize:128
webgpu.isFallbackAdapter:false
webgpu.preferredCanvasFormat:bgra8unorm
webgpu.features:core-features-and-limits,texture-compression-bc,shader-f16,bgra8unorm-storage
webgpu.wgslLanguageFeatures:readonly_and_readwrite_storage_textures,packed_4x8_integer_dot_product,pointer_composite_access
webgpu.limits.maxTextureDimension1D:16384
webgpu.limits.maxTextureDimension2D:16384
webgpu.limits.maxTextureDimension3D:2048

文件改动

dom/webgpu/Utility.h

新增 WebGPU 指纹读取的公共接口:

cpp 复制代码
bool ReadFpFileValue(std::string_view aKey, std::string& aOutValue);
bool ReadFpFileBool(std::string_view aKey, bool* aOutValue);
bool WebGPUFpEnabled();

为什么改这里:

  • WebGPU 的多个文件都要读 fp.txt,不能每个点各自写一套开关逻辑。
  • Utility.h 已经是 WebGPU 模块公共工具头,放这里能让 InstanceAdapter、IPC 回调和 canvas 类型判断复用同一套语义。

dom/webgpu/Utility.cpp

新增 --fpfile 读取和解析逻辑:

  • 从命令行读取 fpfile switch。
  • 支持 key:valuekey=value 两种格式。
  • 去掉 key/value 两侧空白。
  • 读取失败时返回空配置。
  • WebGPUFpEnabled() 支持显式开关:webgpu.enabled / webgpu_enabled
  • 没有显式开关时,只要存在 webgpu.webgpu_ 前缀字段,也认为 WebGPU 指纹模式启用。

为什么这么改:

  • 之前 WebGPU 是否暴露完全受 Firefox pref、blocklist、后端能力影响。这样会导致 BrowserScan 显示"不支持 WebGPU"。
  • 指纹模式需要独立于真实硬件可用性,只要配置文件声明 WebGPU,就能走后续合成/覆盖逻辑。
  • 支持新旧 key 是为了兼容之前 WebKit 方案里的字段风格,同时符合当前 Firefox 代码里 webgpu.* 的命名习惯。

dom/webgpu/Instance.cpp

改动点:

  • Instance::PrefEnabled():当 WebGPUFpEnabled() 为真时,即使 dom.webgpu.enabled pref 没开,也暴露 navigator.gpu
  • service worker 场景同样允许由 WebGPU 指纹开关启用。
  • Instance::Instance():读取 webgpu.wgslLanguageFeatures / webgpu_wgsl_language_features,覆盖 navigator.gpu.wgslLanguageFeatures
  • Instance::GetPreferredCanvasFormat():读取 webgpu.preferredCanvasFormatwebgpu.canvasFormatwebgpu_preferred_canvas_format,覆盖 navigator.gpu.getPreferredCanvasFormat()
  • Instance::RequestAdapter():在 blocklist、pref 禁用、dxcompiler 不可用等检查里加入 webgpuFpEnabled 例外。
  • 请求 adapter 时把 webgpuFpEnabled 传给底层 wgpu 请求流程,用于允许 no-op fallback。

为什么改这里:

  • Instance 是页面入口,也就是 navigator.gpu 的背后对象。
  • 如果这里不放行,后面 adapter/features/limits 都不会被页面看到。
  • WGSL features 和 preferred canvas format 是 navigator.gpu 级别 API,不属于 adapter,因此必须在 Instance 里覆盖。

dom/webgpu/Adapter.cpp

这是 WebGPU 指纹的核心覆盖点。

新增/修改内容:

  • 增加读取数字、布尔、字符串、limits、features 的辅助函数。
  • AdapterInfo getter 读取 fp.txt
    • vendor
    • architecture
    • device
    • description
    • subgroupMinSize
    • subgroupMaxSize
    • isFallbackAdapter
    • wgpuName 也支持由 webgpu.adapterName / webgpu.description 覆盖。
  • 新增 CreateWebGPUFpAdapterInfo(),用于没有真实 adapter 时创建一个 synthetic adapter 信息。
  • 新增 Adapter::CreateFallback(),用于 requestAdapter() 原本返回 null 时仍能返回一个可用的 adapter 对象。
  • 构造 Adapter 时调用:
    • ApplyWebGPUFpLimits(mLimits)
    • ApplyWebGPUFpFeatures(this, mFeatures)
  • requestDevice() 路径里也调用:
    • MergeWebGPUFpFeatures(this, features)
    • ApplyWebGPUFpLimits(limits)
  • fallback adapter 的 requestDevice() 不再去真实后端创建 device,而是直接合成 Device、queue id 和 limits/features。

为什么改这里:

  • BrowserScan 会同时读取 adapterdevice 两边。如果只改 adapter.limits,但 device.limits 仍是真实值,会出现交叉不一致。
  • adapter info 是最明显的 GPU 指纹来源,必须由 fp.txt 覆盖。
  • features 必须过滤 Firefox 当前真正实现的 feature,避免配置了未实现 feature 后 requestDevice() 失败。
  • fallback adapter 是为了处理真实机器、blocklist、驱动、后端不满足时 requestAdapter() 返回 null 的情况。

dom/webgpu/SupportedLimits.h

新增 limit override 存储:

cpp 复制代码
std::array<uint64_t, kLimitCount> mLimitOverrides;
std::array<bool, kLimitCount> mHasLimitOverride;
void SetLimitOverride(Limit, uint64_t);
uint64_t GetLimitValue(Limit) const;

为什么改这里:

  • WebGPU limits 的 getter 都走 SupportedLimits
  • 直接改底层 ffi::WGPULimits 不够,因为有些 JS 暴露字段是合成字段,例如 maxBindGroupsPlusVertexBuffers
  • 用 override 数组可以只覆盖 fp.txt 配置过的字段,没有配置的字段仍保留原来的默认/真实值。

dom/webgpu/SupportedLimits.cpp

实现 GetLimitValue()SetLimitOverride()

改动语义:

  • 如果某个 limit 有 fp override,就优先返回 override。
  • 否则返回原始 ffi::WGPULimits 中的值。

为什么改这里:

  • adapter.limits.xxxdevice.limits.xxx 都最终通过这些 getter 暴露给 JS。
  • 把覆盖集中在这里,可以避免每个 limit getter 单独写判断。

dom/webgpu/ipc/WebGPUChild.cpp

改动 wgpu_child_resolve_request_adapter_promise()

  • 原本底层返回 aAdapterInfo == nullptr 时,promise resolve 为 null
  • 现在如果 WebGPUFpEnabled() 为真,会创建 Adapter::CreateFallback()
  • 如果页面显式请求 forceFallbackAdapter,会检查 isFallbackAdapter 配置,避免语义冲突。

为什么改这里:

  • 很多"WebGPU 不支持"的最终表现就是 requestAdapter() resolve 为 null。
  • 在这个 IPC 回调点处理,可以覆盖真实后端失败、NOOP 后端失败、blocklist 等导致的 null adapter。

dom/canvas/CanvasUtils.cpp

改动 canvas context 类型判断:

cpp 复制代码
if (gfxVars::AllowWebGPU() || mozilla::webgpu::WebGPUFpEnabled()) {
  if (str.EqualsLiteral("webgpu")) {
    *out_type = dom::CanvasContextType::WebGPU;
    return true;
  }
}

为什么改这里:

  • BrowserScan 会检查 document.createElement("canvas").getContext("webgpu")
  • 只暴露 navigator.gpu 不够,canvas 的 webgpu context 也必须可识别。

gfx/wgpu_bindings/src/client.rs

wgpu_client_request_adapter() 增加 allow_noop_fallback 参数,并通过 IPC message 传给 server。

为什么改这里:

  • C++ 层知道当前是否处于 WebGPU fp 模式,但真正选择后端是在 Rust wgpu server 侧。
  • 需要把"允许 no-op fallback"的意图从 C++ 传到 Rust。

gfx/wgpu_bindings/src/server.rs

Message::RequestAdapter 增加 allow_noop_fallback 字段。

改动逻辑:

  • 如果 allow_noop_fallback 为真,优先尝试 Backends::NOOP
  • 之后仍保留 Windows DX12/WebRender adapter、正常 backend 等原有流程。

为什么改这里:

  • NOOP 后端可以在没有真实 GPU adapter 或真实 adapter 不可用时,让 WebGPU 请求流程继续走下去。
  • BrowserScan 主要检查 JS 暴露面,不一定会做复杂 GPU 工作;NOOP fallback 足以支撑基础 API 指纹检查。

ruyi_webgpu_probe.py

扩展本地探针:

  • 设置 WebGPU 相关 prefs。
  • 使用 --fpfile=C:\firefox\fp.txt 启动当前构建的 Firefox。
  • 检查:
    • isSecureContext
    • navigator.gpu
    • canvas.getContext("webgpu")
    • requestAdapter()
    • adapter.info
    • adapter.features
    • adapter.limits
    • requestDevice()
    • device.features
    • device.limits
    • device.adapterInfo
    • wgslLanguageFeatures
    • preferredCanvasFormat
  • 后续也加入了 WebGL 和 Touch 探针,方便看 BrowserScan 相关的整体一致性。

为什么改这里:

  • BrowserScan 是线上站点,结果受页面脚本变化、网络和 profile 状态影响。
  • 本地 probe 能快速确认 Firefox JS 暴露面是否已经按 fp.txt 输出。

当前支持的 WebGPU key

信息类:

txt 复制代码
webgpu.enabled
webgpu.vendor
webgpu.architecture
webgpu.device
webgpu.description
webgpu.subgroupMinSize
webgpu.subgroupMaxSize
webgpu.isFallbackAdapter
webgpu.adapterName

集合类:

txt 复制代码
webgpu.features
webgpu.wgslLanguageFeatures

canvas:

txt 复制代码
webgpu.preferredCanvasFormat
webgpu.canvasFormat

limits 类推荐使用:

txt 复制代码
webgpu.limits.maxTextureDimension1D
webgpu.limits.maxTextureDimension2D
webgpu.limits.maxTextureDimension3D
webgpu.limits.maxTextureArrayLayers
webgpu.limits.maxBindGroups
webgpu.limits.maxBindGroupsPlusVertexBuffers
webgpu.limits.maxBindingsPerBindGroup
webgpu.limits.maxDynamicUniformBuffersPerPipelineLayout
webgpu.limits.maxDynamicStorageBuffersPerPipelineLayout
webgpu.limits.maxSampledTexturesPerShaderStage
webgpu.limits.maxSamplersPerShaderStage
webgpu.limits.maxStorageBuffersInVertexStage
webgpu.limits.maxStorageBuffersInFragmentStage
webgpu.limits.maxStorageBuffersPerShaderStage
webgpu.limits.maxStorageTexturesInVertexStage
webgpu.limits.maxStorageTexturesInFragmentStage
webgpu.limits.maxStorageTexturesPerShaderStage
webgpu.limits.maxUniformBuffersPerShaderStage
webgpu.limits.maxUniformBufferBindingSize
webgpu.limits.maxStorageBufferBindingSize
webgpu.limits.minUniformBufferOffsetAlignment
webgpu.limits.minStorageBufferOffsetAlignment
webgpu.limits.maxVertexBuffers
webgpu.limits.maxBufferSize
webgpu.limits.maxVertexAttributes
webgpu.limits.maxVertexBufferArrayStride
webgpu.limits.maxInterStageShaderVariables
webgpu.limits.maxColorAttachments
webgpu.limits.maxColorAttachmentBytesPerSample
webgpu.limits.maxComputeWorkgroupStorageSize
webgpu.limits.maxComputeInvocationsPerWorkgroup
webgpu.limits.maxComputeWorkgroupSizeX
webgpu.limits.maxComputeWorkgroupSizeY
webgpu.limits.maxComputeWorkgroupSizeZ
webgpu.limits.maxComputeWorkgroupsPerDimension

一致性原则

WebGPU 指纹不要孤立配置。建议和 WebGL 保持同一个 GPU 画像:

  • webgpu.vendorwebgl.unmasked_vendor 不要冲突。
  • webgpu.device / webgpu.descriptionwebgl.renderer 不要表现成完全不同显卡。
  • limits 不要超过该 GPU/浏览器组合常见上限。
  • features 不要配置 Firefox 当前未实现的枚举,否则高级探针可以通过 requestDevice() 或 shader/resource 创建发现不一致。

已知边界

当前方案主要覆盖 BrowserScan 常见的 WebGPU JS 暴露面,不等于完整模拟真实 GPU。

仍可能被深度探测的点:

  • 真实 shader 编译和 WGSL validation 行为。
  • 复杂 texture/buffer/pipeline 创建行为。
  • ChromeOnly 的部分 wgpu* 字段仍可能来自真实后端或 fallback 默认值。
  • subgroupsdual-source-blendingprimitive-index 等 Firefox 未实现 feature 会被过滤。
  • NOOP fallback 可以支撑基础 API,但不能等价于真实 DX12/AMD 后端。

测试方法

编译后运行本地探针:

powershell 复制代码
cd C:\firefox\firefox
python ruyi_webgpu_probe.py

重点确认:

  • hasNavigatorGputrue
  • canvas.webgpuContextAvailabletrue
  • adapterErrornull
  • adapter.infofp.txt 一致。
  • adapter.featuresdevice.features 一致。
  • adapter.limitsdevice.limits 一致。
  • preferredCanvasFormatfp.txt 一致。

BrowserScan 测试时,重点看 WebGPU 是否从"不支持"变成有 adapter、features、limits 和 canvas format。

相关推荐
techdashen1 小时前
你想在 Rust 中实现动态库热重载?
开发语言·chrome·rust
着迷不白3 小时前
六、Bash Shell 与进程管理
前端·chrome
worxfr3 小时前
Linux 磁盘空间排查与清理指南
linux·运维·chrome
王琦03184 小时前
shell 第二章 变量和引用
前端·chrome
Xpower 175 小时前
Codex 桌面端更新后 Chrome 插件和 Computer Use 不可用,怎么排查和修复
前端·人工智能·chrome·python·学习
剑神一笑13 小时前
Linux pgrep 命令详解:按名称查找进程 PID 的高效方法
linux·运维·chrome
剑神一笑13 小时前
Linux killall 命令详解:按进程名批量终止进程的原理与实践
linux·运维·chrome
守城小轩19 小时前
Chromium 146 编译指南 Windows篇:获取源代码(四)
chrome devtools·浏览器自动化·指纹浏览器·浏览器开发
嵌入式小站1 天前
STM32 零基础可移植教程 14:ADC 单通道采样,不接电位器也能读电压
chrome·stm32·嵌入式硬件