Firefox · Networking
CVE-2026-117862
Logic Error in Networking
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifnetwerk/protocol/http/HttpBaseChannel.cpp |
modified | |
ifnetwerk/protocol/http/HttpChannelParent.cpp |
modified |
Files Changed
netwerk/protocol/http/CacheEntryWriteHandleParent.hnetwerk/protocol/http/HttpBaseChannel.cppnetwerk/protocol/http/HttpBaseChannel.hnetwerk/protocol/http/HttpChannelParent.cppnetwerk/protocol/http/HttpChannelParent.hnetwerk/protocol/http/nsHttpChannel.cpp
Patch
diff --git a/netwerk/protocol/http/CacheEntryWriteHandleParent.h b/netwerk/protocol/http/CacheEntryWriteHandleParent.h
index e97149e410b..cee26c82fe6 100644
--- a/netwerk/protocol/http/CacheEntryWriteHandleParent.h
+++ b/netwerk/protocol/http/CacheEntryWriteHandleParent.h
@@ -9,6 +9,7 @@
#include "nsCOMPtr.h"
#include "nsICacheEntry.h"
#include "nsICacheInfoChannel.h"
+#include "nsString.h"
namespace mozilla {
namespace net {
@@ -22,12 +23,16 @@ class CacheEntryWriteHandleParent final : public nsICacheEntryWriteHandle,
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICACHEENTRYWRITEHANDLE
- explicit CacheEntryWriteHandleParent(nsICacheEntry* aCacheEntry);
+ CacheEntryWriteHandleParent(nsICacheEntry* aCacheEntry,
+ const nsACString& aBindingOrigin);
private:
virtual ~CacheEntryWriteHandleParent() = default;
nsCOMPtr<nsICacheEntry> mCacheEntry;
+ // Origin of the principal that requested this write handle, used to bind
+ // any alt-data written through it to that principal.
+ nsCString mBindingOrigin;
};
} // namespace net
diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp
index b2d86d03eaf..cf05679d4d8 100644
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -4483,6 +4483,22 @@ bool HttpBaseChannel::IsNavigation() {
return LoadForceMainDocumentChannel() || (mLoadFlags & LOAD_DOCUMENT_URI);
}
+nsresult HttpBaseChannel::GetAltDataBindingOrigin(nsACString& aOrigin) {
+ aOrigin.Truncate();
+ if (!mLoadInfo) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = mLoadInfo->GetLoadingPrincipal();
+ if (!principal) {
+ principal = mLoadInfo->TriggeringPrincipal();
+ }
+ if (!principal) {
+ return NS_ERROR_FAILURE;
+ }
+ return principal->GetOrigin(aOrigin);
+}
+
bool HttpBaseChannel::BypassServiceWorker() const {
return mLoadFlags & LOAD_BYPASS_SERVICE_WORKER;
}
diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h
index a5ea09db752..4d3dfea93f4 100644
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -504,6 +504,10 @@ class HttpBaseChannel : public nsHashPropertyBag,
bool IsDeliveringAltData() const { return LoadDeliveringAltData(); }
+ // Computes the origin used to bind alternative (bytecode) cache data to the
+ // principal that produced it.
+ nsresult GetAltDataBindingOrigin(nsACString& aOrigin);
+
static void PropagateReferenceIfNeeded(nsIURI* aURI,
nsCOMPtr<nsIURI>& aRedirectURI);
diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp
index 21eea2440e5..88f1dedcaec 100644
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1275,6 +1275,11 @@ HttpChannelParent::OnStartRequest(nsIRequest* aRequest) {
mCacheEntry = do_QueryInterface(cacheEntry);
args.cacheEntryAvailable() = static_cast<bool>(mCacheEntry);
+ // Capture the load's principal origin now, while the channel is alive, to
+ // bind any alt-data written later (after the channel may have been
+ // cleared).
+ chan->GetAltDataBindingOrigin(mAltDataBindingOrigin);
+
httpChannelImpl->GetCacheKey(&args.cacheKey());
httpChannelImpl->GetAlternativeDataType(args.altDataType());
}
@@ -2012,9 +2017,26 @@ NS_INTERFACE_MAP_BEGIN(CacheEntryWriteHandleParent)
NS_INTERFACE_MAP_ENTRY(nsICacheEntryWriteHandle)
NS_INTERFACE_MAP_END
+// Records, atomically with the alt-data write, the origin of the principal
+// that produced the alt-data.
+static void RecordAltDataPrincipal(nsICacheEntry* aCacheEntry,
+ const nsACString& aOrigin) {
+ if (!aCacheEntry) {
+ return;
+ }
+ if (aOrigin.IsEmpty()) {
+ // Fail closed: without a known principal, clear any binding so that reads
+ // reject this alt-data.
+ aCacheEntry->SetMetaDataElement("alt-data-principal", nullptr);
+ } else {
+ aCacheEntry->SetMetaDataElement("alt-data-principal",
+ PromiseFlatCString(aOrigin).get());
+ }
+}
+
CacheEntryWriteHandleParent::CacheEntryWriteHandleParent(
- nsICacheEntry* aCacheEntry)
- : mCacheEntry(aCacheEntry) {}
+ nsICacheEntry* aCacheEntry, const nsACString& aBindingOrigin)
+ : mCacheEntry(aCacheEntry), mBindingOrigin(aBindingOrigin) {}
NS_IMETHODIMP
CacheEntryWriteHandleParent::OpenAlternativeOutputStream(
@@ -2028,12 +2050,13 @@ CacheEntryWriteHandleParent::OpenAlternativeOutputStream(
mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
if (NS_SUCCEEDED(rv)) {
mCacheEntry->SetMetaDataElement("alt-data-from-child", "1");
+ RecordAltDataPrincipal(mCacheEntry, mBindingOrigin);
}
return rv;
}
CacheEntryWriteHandleParent* HttpChannelParent::AllocCacheEntryWriteHandle() {
- return new CacheEntryWriteHandleParent(mCacheEntry);
+ return new CacheEntryWriteHandleParent(mCacheEntry, mAltDataBindingOrigin);
}
nsresult HttpChannelParent::OpenAlternativeOutputStream(
@@ -2048,6 +2071,7 @@ nsresult HttpChannelParent::OpenAlternativeOutputStream(
mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
if (NS_SUCCEEDED(rv)) {
mCacheEntry->SetMetaDataElement("alt-data-from-child", "1");
+ RecordAltDataPrincipal(mCacheEntry, mAltDataBindingOrigin);
}
return rv;
}
diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h
index f48f3e9294f..a01de32e8f1 100644
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -266,6 +266,10 @@ class HttpChannelParent final : public nsIInterfaceRequestor,
RefPtr<HttpBaseChannel> mChannel;
nsCOMPtr<nsICacheEntry> mCacheEntry;
+ // Origin of the load's principal, captured while the channel is still alive
+ // (mChannel is cleared on document channel cleanup, before alt-data is
+ // written). Used to bind child-written alt-data to the producing principal.
+ nsCString mAltDataBindingOrigin;
nsCOMPtr<nsIChannel> mRedirectChannel;
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
index 63df4c9dfa2..6165e474561 100644
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5946,6 +5946,24 @@ nsresult nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry,
nsAutoCString contentType;
mCachedResponseHead->ContentType(contentType);
+ // Alt-data written by a content process (bytecode cache) may only be
+ // consumed by a load whose principal matches the one that produced it.
+ if (altDataFromChild && !altDataType.IsEmpty()) {
+ nsAutoCString storedOrigin;
+ cacheEntry->GetMetaDataElement("alt-data-principal",
+ getter_Copies(storedOrigin));
+ nsAutoCString currentOrigin;
+ GetAltDataBindingOrigin(currentOrigin);
+ if (storedOrigin.IsEmpty() || currentOrigin.IsEmpty() ||
+ !storedOrigin.Equals(currentOrigin)) {
+ LOG(
+ ("Rejecting child-written alt-data due to principal mismatch "
+ "[channel=%p, stored='%s', current='%s']",
+ this, storedOrigin.get(), currentOrigin.get()));
+ altDataType.Truncate();
+ }
+ }
+
bool foundAltData = false;
bool deliverAltData = true;
if (!LoadDisableAltDataCache() && !altDataType.IsEmpty() &&
Loading diff…
References
On This Page