Medium firefox Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSame-origin policy bypass in the Networking: DNS component
ComponentNetworking
Bug ClassCross Origin
Tracker2041001
Fix commitaf41e0702e11 (firefox) +3/-0
CISA KEVNot listed
CreditedRintaro Kawasugi
Disclosed2026-07-21

Background

`DNSPacket`
Gecko’s networking class that parses raw DNS-over-HTTPS (DoH/TRR) response packets into resolved records.
`DecodeInternal`
the method on DNSPacket that walks an incoming DNS wire-format buffer and fills out addresses, a canonical name, and a typed result.
`aTypeResult`
an out-parameter mozilla::Variant holding a “type record” (e.g. an HTTPS/SVCB or TXT record) decoded from the packet, defaulting to Nothing().
Same-origin policy
the browser rule that data resolved or fetched for one origin must not be exposed to a different origin.

Root Cause Analysis

The vulnerable path was DNSPacket::DecodeInternal, which is invoked to decode a DNS response into aTypeResult but never reset aTypeResult to Nothing() at entry. When the same DNSPacket/output slot was reused across decodes — or a later decode failed or contained no matching type record — the aTypeResult variant retained the type record accumulated by a previous decode. This violated the invariant that a decode’s output must reflect only the packet being parsed for the current request, so a stale record from one lookup could be surfaced as the answer for another.

The fix adds aTypeResult = mozilla::AsVariant(Nothing()); immediately after clearing aCname, guaranteeing the typed result starts empty on every call. Because the reset happens before any early-return error path (aLen < 12, etc.), no leftover record can survive a decode that does not legitimately produce one.

Key insight
The single core mistake was failing to reinitialize the aTypeResult out-parameter at the start of DecodeInternal, letting a previous decode’s type record leak into an unrelated result; the fix forces aTypeResult back to Nothing() up front so each decode starts from a clean state.

Attack Path

  1. Trigger a first resolution The attacker’s origin causes a DNS/DoH lookup that decodes into a valid type record stored in aTypeResult.
  2. Reuse the decode path A subsequent lookup reuses the same DNSPacket output slot, decoding a response that carries no matching type record or hits an early return.
  3. Stale record survives Because aTypeResult was never reset, the previous decode’s type record remains in the variant instead of Nothing().
  4. Cross-origin exposure The stale type record is consumed as the answer for the second, different-origin request, bypassing the same-origin boundary between the two resolutions.

Impact Assessment

An attacker can cause a DNS type record resolved in one context to be surfaced as the result of an unrelated resolution, producing a same-origin-policy bypass in the DNS/networking layer of the browser process. The severity is moderate: it requires the reuse of the decode path across resolutions where the second response lacks its own type record, and yields disclosure/confusion of resolved record data rather than direct memory corruption.

Changed Functions

FunctionChangeNotes
if
netwerk/dns/DNSPacket.cpp
modified

Files Changed

  • netwerk/dns/DNSPacket.cpp

Audit Directions

  • Uninitialized out-parameters
    Audit decode/parse routines whose callers reuse output buffers and confirm every out-parameter (especially Variant/Maybe/Optional types like aTypeResult) is reset before any early-return path.
  • Cross-request state reuse
    Review DNSPacket and sibling networking parsers for object or buffer reuse across resolutions where residual state from one request could leak into another.
  • Early-return leakage
    For functions with multiple failure exits, verify that clearing/initialization occurs before the first return, not after, so error paths cannot surface stale results.
diff --git a/netwerk/dns/DNSPacket.cpp b/netwerk/dns/DNSPacket.cpp
index 2fd99eab098..4578c7692b2 100644
--- a/netwerk/dns/DNSPacket.cpp
+++ b/netwerk/dns/DNSPacket.cpp
@@ -646,6 +646,9 @@ nsresult DNSPacket::DecodeInternal(
 
   aCname.Truncate();
 
+  // Reset any type record accumulated by a previous decode of this packet.
+  aTypeResult = mozilla::AsVariant(Nothing());
+
   if (aLen < 12) {
     LOG(("TRR bad incoming DOH, eject!\n"));
     return NS_ERROR_ILLEGAL_VALUE;
Loading diff…