Firefox · DOM
CVE-2026-117874
Logic Error in DOM
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ProcessTypedArraysFixeddom/compression/BaseAlgorithms.cpp |
modified |
Files Changed
dom/compression/BaseAlgorithms.cppdom/compression/BaseAlgorithms.hdom/encoding/TextDecoderStream.cppdom/encoding/TextEncoderStream.cppdom/streams/TransformerCallbackHelpers.cppdom/streams/TransformerCallbackHelpers.htesting/web-platform/tests/encoding/streams/crashtests/from-cross-realm.html
Patch
diff --git a/dom/compression/BaseAlgorithms.cpp b/dom/compression/BaseAlgorithms.cpp
index b680d57bbdc..d5fe06684d6 100644
--- a/dom/compression/BaseAlgorithms.cpp
+++ b/dom/compression/BaseAlgorithms.cpp
@@ -17,36 +17,29 @@ namespace mozilla::dom::compression {
// runs the compress and enqueue a chunk algorithm with this and chunk.
MOZ_CAN_RUN_SCRIPT
void CompressionStreamAlgorithms::TransformCallbackImpl(
- JS::Handle<JS::Value> aChunk, TransformStreamDefaultController& aController,
- ErrorResult& aRv) {
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
+ TransformStreamDefaultController& aController, ErrorResult& aRv) {
// https://compression.spec.whatwg.org/#compress-and-enqueue-a-chunk
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
- RootedUnion<OwningBufferSource> bufferSource(cx);
- if (!bufferSource.Init(cx, aChunk)) {
+ RootedUnion<OwningBufferSource> bufferSource(aCx);
+ if (!bufferSource.Init(aCx, aChunk)) {
aRv.MightThrowJSException();
- aRv.StealExceptionFromJSContext(cx);
+ aRv.StealExceptionFromJSContext(aCx);
return;
}
// Steps 2 - 4:
- JS::RootedVector<JSObject*> array(cx);
+ JS::RootedVector<JSObject*> array(aCx);
ProcessTypedArraysFixed(bufferSource, [&](const Span<uint8_t>& aData) {
- Compress(cx, aData, &array, Flush::No, aRv);
+ Compress(aCx, aData, &array, Flush::No, aRv);
});
if (aRv.Failed()) {
return;
}
// Step 5: For each Uint8Array array, enqueue array in cs's transform.
- Enqueue(cx, array, aController, aRv);
+ Enqueue(aCx, array, aController, aRv);
}
// Step 4 of
@@ -54,24 +47,18 @@ void CompressionStreamAlgorithms::TransformCallbackImpl(
// Let flushAlgorithm be an algorithm which takes no argument and runs the
// compress flush and enqueue algorithm with this.
MOZ_CAN_RUN_SCRIPT void CompressionStreamAlgorithms::FlushCallbackImpl(
- TransformStreamDefaultController& aController, ErrorResult& aRv) {
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
+ JSContext* aCx, TransformStreamDefaultController& aController,
+ ErrorResult& aRv) {
// https://compression.spec.whatwg.org/#compress-flush-and-enqueue
// Step 1-3:
- JS::RootedVector<JSObject*> arrays(cx);
- Compress(cx, Span<const uint8_t>(), &arrays, Flush::Yes, aRv);
+ JS::RootedVector<JSObject*> arrays(aCx);
+ Compress(aCx, Span<const uint8_t>(), &arrays, Flush::Yes, aRv);
if (aRv.Failed()) {
return;
}
// Step 4: For each Uint8Array array, enqueue array in cs's transform.
- Enqueue(cx, arrays, aController, aRv);
+ Enqueue(aCx, arrays, aController, aRv);
}
MOZ_CAN_RUN_SCRIPT void CompressionStreamAlgorithms::Enqueue(
@@ -93,22 +80,15 @@ MOZ_CAN_RUN_SCRIPT void CompressionStreamAlgorithms::Enqueue(
// runs the compress and enqueue a chunk algorithm with this and chunk.
MOZ_CAN_RUN_SCRIPT
void DecompressionStreamAlgorithms::TransformCallbackImpl(
- JS::Handle<JS::Value> aChunk, TransformStreamDefaultController& aController,
- ErrorResult& aRv) {
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
+ TransformStreamDefaultController& aController, ErrorResult& aRv) {
// https://compression.spec.whatwg.org/#decompress-and-enqueue-a-chunk
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
- RootedUnion<OwningBufferSource> bufferSource(cx);
- if (!bufferSource.Init(cx, aChunk)) {
+ RootedUnion<OwningBufferSource> bufferSource(aCx);
+ if (!bufferSource.Init(aCx, aChunk)) {
aRv.MightThrowJSException();
- aRv.StealExceptionFromJSContext(cx);
+ aRv.StealExceptionFromJSContext(aCx);
return;
}
@@ -117,17 +97,17 @@ void DecompressionStreamAlgorithms::TransformCallbackImpl(
// TypeError.
// Step 4: Let arrays be the result of splitting buffer into one or more
// non-empty pieces and converting them into Uint8Arrays.
- JS::RootedVector<JSObject*> array(cx);
+ JS::RootedVector<JSObject*> array(aCx);
bool fullyConsumed = false;
ProcessTypedArraysFixed(bufferSource, [&](const Span<uint8_t>& aData) {
- fullyConsumed = Decompress(cx, aData, &array, Flush::No, aRv);
+ fullyConsumed = Decompress(aCx, aData, &array, Flush::No, aRv);
});
if (aRv.Failed()) {
return;
}
// Step 5: For each Uint8Array array, enqueue array in ds's transform.
- Enqueue(cx, array, aController, aRv);
+ Enqueue(aCx, array, aController, aRv);
if (aRv.Failed()) {
return;
}
@@ -144,25 +124,19 @@ void DecompressionStreamAlgorithms::TransformCallbackImpl(
// Let flushAlgorithm be an algorithm which takes no argument and runs the
// compress flush and enqueue algorithm with this.
MOZ_CAN_RUN_SCRIPT void DecompressionStreamAlgorithms::FlushCallbackImpl(
- TransformStreamDefaultController& aController, ErrorResult& aRv) {
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
- JS::RootedVector<JSObject*> array(cx);
+ JSContext* aCx, TransformStreamDefaultController& aController,
+ ErrorResult& aRv) {
+ JS::RootedVector<JSObject*> array(aCx);
// https://compression.spec.whatwg.org/#decompress-flush-and-enqueue
// Step 1 and 2.1:
- (void)Decompress(cx, Span<const uint8_t>(), &array, Flush::Yes, aRv);
+ (void)Decompress(aCx, Span<const uint8_t>(), &array, Flush::Yes, aRv);
if (aRv.Failed()) {
return;
}
// Step 2.2: For each Uint8Array array, enqueue array in ds's transform.
- Enqueue(cx, array, aController, aRv);
+ Enqueue(aCx, array, aController, aRv);
if (aRv.Failed()) {
return;
}
diff --git a/dom/compression/BaseAlgorithms.h b/dom/compression/BaseAlgorithms.h
index b99c2fefe1f..1deb2b1190d 100644
--- a/dom/compression/BaseAlgorithms.h
+++ b/dom/compression/BaseAlgorithms.h
@@ -22,7 +22,7 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
// Let transformAlgorithm be an algorithm which takes a chunk argument and
// runs the compress and enqueue a chunk algorithm with this and chunk.
MOZ_CAN_RUN_SCRIPT
- void TransformCallbackImpl(JS::Handle<JS::Value> aChunk,
+ void TransformCallbackImpl(JSContext* aCx, JS::Handle<JS::Value> aChunk,
TransformStreamDefaultController& aController,
ErrorResult& aRv) override;
@@ -31,7 +31,8 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
// Let flushAlgorithm be an algorithm which takes no argument and runs the
// compress flush and enqueue algorithm with this.
MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
- TransformStreamDefaultController& aController, ErrorResult& aRv) override;
+ JSContext* aCx, TransformStreamDefaultController& aController,
+ ErrorResult& aRv) override;
protected:
static const uint16_t kBufferSize = 16384;
@@ -60,7 +61,7 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
// Let transformAlgorithm be an algorithm which takes a chunk argument and
// runs the compress and enqueue a chunk algorithm with this and chunk.
MOZ_CAN_RUN_SCRIPT
- void TransformCallbackImpl(JS::Handle<JS::Value> aChunk,
+ void TransformCallbackImpl(JSContext* aCx, JS::Handle<JS::Value> aChunk,
TransformStreamDefaultController& aController,
ErrorResult& aRv) override;
@@ -69,7 +70,8 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
// Let flushAlgorithm be an algorithm which takes no argument and runs the
// compress flush and enqueue algorithm with this.
MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
- TransformStreamDefaultController& aController, ErrorResult& aRv) override;
+ JSContext* aCx, TransformStreamDefaultController& aController,
+ ErrorResult& aRv) override;
protected:
static const uint16_t kBufferSize = 16384;
diff --git a/dom/encoding/TextDecoderStream.cpp b/dom/encoding/TextDecoderStream.cpp
index 607dd101f3a..f12c5305fdb 100644
--- a/dom/encoding/TextDecoderStream.cpp
+++ b/dom/encoding/TextDecoderStream.cpp
@@ -93,7 +93,7 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
// https://encoding.spec.whatwg.org/#dom-textdecoderstream
MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl(
- JS::Handle<JS::Value> aChunk,
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
TransformStreamDefaultController& aController,
ErrorResult& aRv) override {
// Step 7. Let transformAlgorithm be an algorithm which takes a chunk
@@ -102,43 +102,29 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
// https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
// Step 1. Let bufferSource be the result of converting chunk to an
// [AllowShared] BufferSource.
- RootedUnion<OwningBufferSource> bufferSource(cx);
- if (!bufferSource.Init(cx, aChunk)) {
+ RootedUnion<OwningBufferSource> bufferSource(aCx);
+ if (!bufferSource.Init(aCx, aChunk)) {
aRv.MightThrowJSException();
- aRv.StealExceptionFromJSContext(cx);
+ aRv.StealExceptionFromJSContext(aCx);
return;
}
- DecodeBufferSourceAndEnqueue(cx, &bufferSource, false, aController, aRv);
+ DecodeBufferSourceAndEnqueue(aCx, &bufferSource, false, aController, aRv);
}
// https://encoding.spec.whatwg.org/#dom-textdecoderstream
MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
- TransformStreamDefaultController& aController,
+ JSContext* aCx, TransformStreamDefaultController& aController,
ErrorResult& aRv) override {
// Step 8. Let flushAlgorithm be an algorithm which takes no arguments and
// runs the flush and enqueue algorithm with this.
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
// https://encoding.spec.whatwg.org/#flush-and-enqueue
// (The flush and enqueue algorithm is basically a subset of decode and
// enqueue one, so let's reuse it)
- DecodeBufferSourceAndEnqueue(cx, nullptr, true, aController, aRv);
+ DecodeBufferSourceAndEnqueue(aCx, nullptr, true, aController, aRv);
}
private:
diff --git a/dom/encoding/TextEncoderStream.cpp b/dom/encoding/TextEncoderStream.cpp
index d275802c264..9296c11dbba 100644
--- a/dom/encoding/TextEncoderStream.cpp
+++ b/dom/encoding/TextEncoderStream.cpp
@@ -139,51 +139,37 @@ class TextEncoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
// https://encoding.spec.whatwg.org/#dom-textencoderstream
MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl(
- JS::Handle<JS::Value> aChunk,
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
TransformStreamDefaultController& aController,
ErrorResult& aRv) override {
// Step 2. Let transformAlgorithm be an algorithm which takes a chunk
// argument and runs the encode and enqueue a chunk algorithm with this and
// chunk.
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
// https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
// Step 1. Let input be the result of converting chunk to a DOMString.
// Step 2. Convert input to an I/O queue of code units.
nsString str;
- if (!ConvertJSValueToString(cx, aChunk, eStringify, eStringify, str)) {
+ if (!ConvertJSValueToString(aCx, aChunk, eStringify, eStringify, str)) {
aRv.MightThrowJSException();
- aRv.StealExceptionFromJSContext(cx);
+ aRv.StealExceptionFromJSContext(aCx);
return;
}
- EncodeAndEnqueue(cx, str, aController, false, aRv);
+ EncodeAndEnqueue(aCx, str, aController, false, aRv);
}
// https://encoding.spec.whatwg.org/#dom-textencoderstream
MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
- TransformStreamDefaultController& aController,
+ JSContext* aCx, TransformStreamDefaultController& aController,
ErrorResult& aRv) override {
// Step 3. Let flushAlgorithm be an algorithm which runs the encode and
// flush algorithm with this.
- AutoJSAPI jsapi;
- if (!jsapi.Init(aController.GetParentObject())) {
- aRv.ThrowUnknownError("Internal error");
- return;
- }
- JSContext* cx = jsapi.cx();
-
// The spec manually manages pending high surrogate here, but let's call the
// encoder as it's managed there.
- EncodeAndEnqueue(cx, u""_ns, aController, true, aRv);
+ EncodeAndEnqueue(aCx, u""_ns, aController, true, aRv);
}
private:
diff --git a/dom/streams/TransformerCallbackHelpers.cpp b/dom/streams/TransformerCallbackHelpers.cpp
index 18780d28772..379a546cb8b 100644
--- a/dom/streams/TransformerCallbackHelpers.cpp
+++ b/dom/streams/TransformerCallbackHelpers.cpp
@@ -5,7 +5,9 @@
#include "TransformerCallbackHelpers.h"
#include "StreamUtils.h"
+#include "jsapi.h"
#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TransformStreamDefaultController.h"
using namespace mozilla::dom;
@@ -84,26 +86,42 @@ already_AddRefed<Promise> TransformerAlgorithms::FlushCallback(
}
already_AddRefed<Promise> TransformerAlgorithmsWrapper::TransformCallback(
- JSContext*, JS::Handle<JS::Value> aChunk,
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
TransformStreamDefaultController& aController, ErrorResult& aRv) {
+ // We are in the writer stream's realm, but we want to call the transform
+ // algorithm in the transform stream's realm to make sure the generated object
+ // are from the right realm.
nsCOMPtr<nsIGlobalObject> global = aController.GetParentObject();
+ JSAutoRealm realm(aCx, global->GetGlobalJSObject());
+
+ JS::Rooted<JS::Value> chunk(aCx, aChunk);
+ if (!JS_WrapValue(aCx, &chunk)) {
+ aRv.MightThrowJSException();
+ aRv.StealExceptionFromJSContext(aCx);
+ return nullptr;
+ }
+
return PromisifyAlgorithm(
global,
- [this, &aChunk, &aController](ErrorResult& aRv)
+ [this, aCx, &chunk, &aController](ErrorResult& aRv)
MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
- return TransformCallbackImpl(aChunk, aController, aRv);
+ return TransformCallbackImpl(aCx, chunk, aController, aRv);
},
aRv);
}
already_AddRefed<Promise> TransformerAlgorithmsWrapper::FlushCallback(
- JSContext*, TransformStreamDefaultController& aController,
+ JSContext* aCx, TransformStreamDefaultController& aController,
ErrorResult& aRv) {
+ // Same potential realm difference, see also TransformCallback above
nsCOMPtr<nsIGlobalObject> global = aController.GetParentObject();
+ JSAutoRealm realm(aCx, global->GetGlobalJSObject());
+
return PromisifyAlgorithm(
global,
- [this, &aController](ErrorResult& aRv) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
- return FlushCallbackImpl(aController, aRv);
- },
+ [this, aCx, &aController](ErrorResult& aRv)
+ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ return FlushCallbackImpl(aCx, aController, aRv);
+ },
aRv);
}
diff --git a/dom/streams/TransformerCallbackHelpers.h b/dom/streams/TransformerCallbackHelpers.h
index 4f8173d2e27..6ebf3831052 100644
--- a/dom/streams/TransformerCallbackHelpers.h
+++ b/dom/streams/TransformerCallbackHelpers.h
@@ -86,11 +86,12 @@ class TransformerAlgorithmsWrapper : public TransformerAlgorithmsBase {
ErrorResult& aRv) final;
MOZ_CAN_RUN_SCRIPT virtual void TransformCallbackImpl(
- JS::Handle<JS::Value> aChunk,
+ JSContext* aCx, JS::Handle<JS::Value> aChunk,
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/testing/web-platform/tests/encoding/streams/crashtests/from-cross-realm.html b/testing/web-platform/tests/encoding/streams/crashtests/from-cross-realm.html
new file mode 100644
index 00000000000..0fcae0e2552
--- /dev/null
+++ b/testing/web-platform/tests/encoding/streams/crashtests/from-cross-realm.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+const encoder = new TextEncoderStream();
+encoder.readable.getReader().read();
+const w = window.open();
+new w.WritableStreamDefaultWriter(encoder.writable).write({});
+</script>
Loading diff…
References
On This Page