Medium firefox Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionFirefox cached CORS preflight responses across IP address changes. This allowed circumventing CORS with DNS rebinding.
ComponentNetworking
Bug ClassCross Origin
Tracker1960834
Fix commit47bb9492b5ec (firefox) +155/-37
CISA KEVNot listed
CreditedViktor Bocz
Disclosed2025-07-22

Changed Functions

FunctionChangeNotes
GetLastUpdate
netwerk/dns/DNSRequestChild.cpp
modified
ChildDNSByTypeRecord
netwerk/dns/DNSRequestChild.cpp
modified
GetTtl
netwerk/dns/nsDNSService2.cpp
modified
GetLastUpdate
netwerk/dns/nsDNSService2.cpp
modified
nsDNSByTypeRecord
netwerk/dns/nsDNSService2.cpp
modified
nsDNSSyncRequest
netwerk/dns/nsDNSService2.cpp
modified
DNSCacheRequest
netwerk/dns/nsDNSService2.cpp
modified
NotifyDNSResolution
netwerk/dns/nsDNSService2.cpp
modified

Files Changed

  • netwerk/dns/DNSRequestChild.cpp
  • netwerk/dns/DNSRequestParent.cpp
  • netwerk/dns/PDNSRequestParams.ipdlh
  • netwerk/dns/nsDNSService2.cpp
  • netwerk/dns/nsHostRecord.cpp
  • netwerk/dns/nsHostRecord.h
  • netwerk/dns/nsHostResolver.cpp
  • netwerk/dns/nsIDNSRecord.idl
  • netwerk/protocol/http/nsCORSListenerProxy.cpp
diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp
index 7b09a24cc9c..b1bc034916e 100644
--- a/netwerk/dns/DNSRequestChild.cpp
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -58,6 +58,7 @@ class ChildDNSRecord : public nsIDNSAddrRecord {
   nsIRequest::TRRMode mEffectiveTRRMode = nsIRequest::TRR_DEFAULT_MODE;
   nsITRRSkipReason::value mTRRSkipReason = nsITRRSkipReason::TRR_UNSET;
   uint32_t mTTL = 0;
+  TimeStamp mLastUpdate = mozilla::TimeStamp::NowLoRes();
 };
 
 NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
@@ -78,6 +79,7 @@ ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply,
   const nsTArray<NetAddr>& addrs = reply.addrs();
   mAddresses = addrs.Clone();
   mTTL = reply.ttl();
+  mLastUpdate = reply.lastUpdate();
 }
 
 //-----------------------------------------------------------------------------
@@ -208,6 +210,12 @@ ChildDNSRecord::GetTtl(uint32_t* aTtl) {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+ChildDNSRecord::GetLastUpdate(TimeStamp* aLastUpdate) {
+  *aLastUpdate = mLastUpdate;
+  return NS_OK;
+}
+
 class ChildDNSByTypeRecord : public nsIDNSByTypeRecord,
                              public nsIDNSTXTRecord,
                              public nsIDNSHTTPSSVCRecord,
diff --git a/netwerk/dns/DNSRequestParent.cpp b/netwerk/dns/DNSRequestParent.cpp
index d004180476b..5bb4f36f8f9 100644
--- a/netwerk/dns/DNSRequestParent.cpp
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -141,10 +141,14 @@ DNSRequestHandler::OnLookupComplete(nsICancelable* request,
     uint32_t ttl = 0;
     rec->GetTtl(&ttl);
 
+    TimeStamp lastUpdate;
+    rec->GetLastUpdate(&lastUpdate);
+
     SendLookupCompletedHelper(
-        mIPCActor, DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration,
-                                                trrFetchDurationNetworkOnly,
-                                                isTRR, effectiveTRRMode, ttl)));
+        mIPCActor,
+        DNSRequestResponse(DNSRecord(cname, array, trrFetchDuration,
+                                     trrFetchDurationNetworkOnly, isTRR,
+                                     effectiveTRRMode, ttl, lastUpdate)));
   } else {
     SendLookupCompletedHelper(mIPCActor, DNSRequestResponse(status));
   }
diff --git a/netwerk/dns/PDNSRequestParams.ipdlh b/netwerk/dns/PDNSRequestParams.ipdlh
index de15e91a59e..98c67d39e7c 100644
--- a/netwerk/dns/PDNSRequestParams.ipdlh
+++ b/netwerk/dns/PDNSRequestParams.ipdlh
@@ -8,6 +8,7 @@
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
 using mozilla::net::IPCTypeRecord from "mozilla/net/DNSByTypeRecord.h";
 using nsIRequest::TRRMode from "nsIRequest.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 
 namespace mozilla {
 namespace net {
@@ -25,6 +26,7 @@ struct DNSRecord
   bool isTRR;
   TRRMode effectiveTRRMode;
   uint32_t ttl;
+  TimeStamp lastUpdate;
 };
 
 union DNSRequestResponse
diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp
index bcf7c9772f4..1b3dd649519 100644
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -364,6 +364,12 @@ NS_IMETHODIMP nsDNSRecord::GetTrrSkipReason(
 NS_IMETHODIMP
 nsDNSRecord::GetTtl(uint32_t* aTtl) { return mHostRecord->GetTtl(aTtl); }
 
+NS_IMETHODIMP
+nsDNSRecord::GetLastUpdate(mozilla::TimeStamp* aLastUpdate) {
+  MutexAutoLock lock(mHostRecord->addr_info_lock);
+  return mHostRecord->GetLastUpdate(aLastUpdate);
+}
+
 class nsDNSByTypeRecord : public nsIDNSByTypeRecord,
                           public nsIDNSTXTRecord,
                           public nsIDNSHTTPSSVCRecord {
@@ -566,18 +572,53 @@ nsDNSAsyncRequest::Cancel(nsresult reason) {
 
 //-----------------------------------------------------------------------------
 
-class nsDNSSyncRequest : public nsResolveHostCallback {
+class DNSCacheRequest : public nsResolveHostCallback {
+ public:
   NS_DECL_THREADSAFE_ISUPPORTS
+
+  DNSCacheRequest() = default;
+
+  void OnResolveHostComplete(nsHostResolver* resolver, nsHostRecord* hostRecord,
+                             nsresult status) override {
+    mStatus = status;
+    mHostRecord = hostRecord;
+  }
+
+  bool EqualsAsyncListener(nsIDNSListener* aListener) override {
+    // Sync request: no listener to compare
+    return false;
+  }
+
+  size_t SizeOfIncludingThis(
+      mozilla::MallocSizeOf mallocSizeOf) const override {
+    size_t n = mallocSizeOf(this);
+
+    // The following fields aren't measured.
+    // - mHostRecord, because it's a non-owning pointer
+
+    // Measurement of the following members may be added later if DMD finds it
+    // is worthwhile:
+    // - nsDNSSyncRequest::mMonitor
+
+    return n;
+  }
+
+  nsresult mStatus = NS_OK;
+  RefPtr<nsHostRecord> mHostRecord;
+
+ protected:
+  virtual ~DNSCacheRequest() = default;
+};
+
+NS_IMPL_ISUPPORTS0(DNSCacheRequest)
+
+class nsDNSSyncRequest : public DNSCacheRequest {
  public:
   explicit nsDNSSyncRequest(PRMonitor* mon) : mMonitor(mon) {}
 
   void OnResolveHostComplete(nsHostResolver*, nsHostRecord*, nsresult) override;
-  bool EqualsAsyncListener(nsIDNSListener* aListener) override;
-  size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const override;
 
   bool mDone = false;
-  nsresult mStatus = NS_OK;
-  RefPtr<nsHostRecord> mHostRecord;
 
  private:
   virtual ~nsDNSSyncRequest() = default;
@@ -585,38 +626,17 @@ class nsDNSSyncRequest : public nsResolveHostCallback {
   PRMonitor* mMonitor = nullptr;
 };
 
-NS_IMPL_ISUPPORTS0(nsDNSSyncRequest)
-
 void nsDNSSyncRequest::OnResolveHostComplete(nsHostResolver* resolver,
                                              nsHostRecord* hostRecord,
                                              nsresult status) {
   // store results, and wake up nsDNSService::Resolve to process results.
   PR_EnterMonitor(mMonitor);
   mDone = true;
-  mStatus = status;
-  mHostRecord = hostRecord;
+  DNSCacheRequest::OnResolveHostComplete(resolver, hostRecord, status);
   PR_Notify(mMonitor);
   PR_ExitMonitor(mMonitor);
 }
 
-bool nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener* aListener) {
-  // Sync request: no listener to compare
-  return false;
-}
-
-size_t nsDNSSyncRequest::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
-  size_t n = mallocSizeOf(this);
-
-  // The following fields aren't measured.
-  // - mHostRecord, because it's a non-owning pointer
-
-  // Measurement of the following members may be added later if DMD finds it
-  // is worthwhile:
-  // - mMonitor
-
-  return n;
-}
-
 class NotifyDNSResolution : public Runnable {
  public:
   explicit NotifyDNSResolution(const nsACString& aHostname)
@@ -1185,8 +1205,10 @@ nsDNSService::ResolveNative(const nsACString& aHostname,
                             nsIDNSService::DNSFlags flags,
                             const OriginAttributes& aOriginAttributes,
                             nsIDNSRecord** result) {
-  // Synchronous resolution is not available on the main thread.
-  if (NS_IsMainThread()) {
+  // Synchronous resolution is not allowed on the main thread.
+  // However, if RESOLVE_OFFLINE is set, we're only reading from the DNS cache,
+  // so it's safe to allow this on the main thread.
Loading diff…