CVE-2026-74942
Overview
Background
- Remote Settings
- Firefox’s mechanism for delivering signed configuration data (blocklists, feature settings) from a Mozilla server to the client.
- `RemoteSettingsClient`
- the client class that syncs remote records into a local
IndexedDBand gates their use on a content-signature check. - `InvalidSignatureError`
- the specific error thrown when the collection’s content signature does not verify against the fetched
x5ucertificate chain. - `verifySignatureLocalData`
- an executor passed to a
Promisethat re-verifies the signature over the data that existed locally before the current sync.
Root Cause Analysis
During maybeSync, remote records are imported into the local database before their signature is verified, so on a verification failure the code must roll those unverified records back. The vulnerable path guarded the rollback behind if (!(e instanceof InvalidSignatureError)) { throw e; }, so any failure that was not exactly an InvalidSignatureError — a malformed signature or a failed x5u cert-chain fetch — escaped the handler and threw before the just-imported unverified records were cleared, leaving them persisted and trusted. A second gap was that verifySignatureLocalData was awaited directly (hasLocalData && (await new Promise(verifySignatureLocalData))), so if that local re-verification itself threw, the exception again skipped the reset-and-clear logic. The invariant violated is that no unverified imported record may remain in the local DB after signature validation fails for any reason.
The fix removes the early throw and wraps the local re-verification in a try/catch that defaults localTrustworthy to false, so every failure class now funnels into the clear/reset-to-dump path instead of leaving poisoned data behind.
InvalidSignatureError as a rollback-worthy failure while letting all other verification errors (malformed signatures, x5u fetch failures, exceptions from local re-verification) bypass cleanup; the fix makes any failure default to untrustworthy and force a clear/reset of the freshly imported records.Attack Path
- Serve tampered data
An attacker positioned to feed the sync response (or a compromised/hijacked local DB) supplies records whose signature fails in a way other than a clean
InvalidSignatureError, e.g. a malformed signature or an unreachable/invalidx5uchain. - Trigger import A sync imports the attacker’s records into the local database before verification runs.
- Escape the rollback
Verification fails with a non-
InvalidSignatureError(or the local re-verification throws), so the originalthrow eexits the handler before the clear/reset step executes. - Persist unverified records The imported, unverified records remain in the local store and are consumed by Remote Settings consumers as if trusted.
- Gain effect The malicious configuration takes effect (e.g. altered blocklists or feature settings), escalating the attacker’s control over privileged browser behavior.
Impact Assessment
InvalidSignatureError verification failure during sync (malformed signature, failed x5u fetch) or a throwing local re-verification.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifservices/settings/RemoteSettingsClient.sys.mjs |
modified |
Files Changed
services/settings/RemoteSettingsClient.sys.mjs
Audit Directions
- Import-before-verifyFlag any sync/update path that writes records to persistent storage before signature or integrity checks complete, and confirm every failure branch rolls the writes back.
- Narrow error-type gatingLook for
instanceof-based guards that treat one error subtype as recoverable while re-throwing (and thereby skipping cleanup for) all others; cleanup should be error-type-agnostic. - Throwing verification helpersEnsure awaited verification promises like
verifySignatureLocalDataare wrapped so an exception maps to “untrustworthy” and still triggers reset, rather than propagating past the cleanup code.
Patch
diff --git a/services/settings/RemoteSettingsClient.sys.mjs b/services/settings/RemoteSettingsClient.sys.mjs
index 3ee4ae65a40..f4f9b1c0bce 100644
--- a/services/settings/RemoteSettingsClient.sys.mjs
+++ b/services/settings/RemoteSettingsClient.sys.mjs
@@ -1226,21 +1226,26 @@ export class RemoteSettingsClient extends EventEmitter {
lazy.console.error(
`${this.identifier} Signature failed ${retry ? "again" : ""} ${e}`
);
- if (!(e instanceof InvalidSignatureError)) {
- // If it failed for any other kind of error (eg. shutdown)
- // then give up quickly.
- throw e;
- }
- // In order to distinguish signature errors that happen
- // during sync, from hijacks of local DBs, we will verify
- // the signature on the data that we had before syncing
- // (if any).
+ // Any verification failure, invalid signature, malformed signature,
+ // or a failed x5u cert-chain fetch, must roll back the records just
+ // imported above, which are still unverified.
if (!hasLocalData) {
lazy.console.debug(`${this.identifier} No previous data to restore`);
}
- const localTrustworthy =
- hasLocalData && (await new Promise(verifySignatureLocalData));
+
+ let localTrustworthy = false;
+ if (hasLocalData) {
+ try {
+ localTrustworthy = await new Promise(verifySignatureLocalData);
+ } catch (_) {
+ // We either throw a CorruptedDataError below which will lead to a
+ // telemetry event, or we have retried already and this is the second
+ // failure. We will reset to dump, clear everything, and throw the error
+ // so that the caller can report the sync status.
+ }
+ }
+
if (!localTrustworthy && !retry) {
// Signature failed, clear local DB because it contains
// bad data (local + remote changes).