From 8dd16259287f58f9273002717ec4d27e97127719 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:43:14 +0200 Subject: Merging upstream version 127.0. Signed-off-by: Daniel Baumann --- dom/indexedDB/IDBFactory.cpp | 171 ++++++++++++----------- dom/indexedDB/IDBFactory.h | 32 ++--- dom/indexedDB/IDBObjectStore.cpp | 22 ++- dom/indexedDB/Key.cpp | 125 +++++++++++++---- dom/indexedDB/Key.h | 8 +- dom/indexedDB/crashtests/1499854-1.html | 2 +- dom/indexedDB/crashtests/1857979-1.html | 4 +- dom/indexedDB/test/perfdocs/index.rst | 2 +- dom/indexedDB/test/unit/test_invalid_version.js | 16 --- dom/indexedDB/test/unit/test_metadata2Restore.js | 40 +++--- dom/indexedDB/test/unit/test_metadataRestore.js | 24 ++-- 11 files changed, 257 insertions(+), 189 deletions(-) (limited to 'dom/indexedDB') diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index c0dc5aeab2..b59efd9124 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -35,6 +35,8 @@ #include "nsIURI.h" #include "nsIUUIDGenerator.h" #include "nsIWebNavigation.h" +#include "nsLiteralString.h" +#include "nsStringFwd.h" #include "nsNetUtil.h" #include "nsSandboxFlags.h" #include "nsServiceManagerUtils.h" @@ -53,50 +55,7 @@ using namespace mozilla::ipc; namespace { -Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT IdentifyPrincipalType( - const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { - switch (aPrincipalInfo.type()) { - case PrincipalInfo::TSystemPrincipalInfo: - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::system; - case PrincipalInfo::TContentPrincipalInfo: { - const ContentPrincipalInfo& info = - aPrincipalInfo.get_ContentPrincipalInfo(); - - nsCOMPtr uri; - - if (NS_WARN_IF(NS_FAILED(NS_NewURI(getter_AddRefs(uri), info.spec())))) { - // This could be discriminated as an extra error value, but this is - // extremely unlikely to fail, so we just misuse ContentOther - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_other; - } - - // TODO Are there constants defined for the schemes somewhere? - if (uri->SchemeIs("file")) { - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_file; - } - if (uri->SchemeIs("http") || uri->SchemeIs("https")) { - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_http_https; - } - if (uri->SchemeIs("moz-extension")) { - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_moz_ext; - } - if (uri->SchemeIs("about")) { - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_about; - } - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT:: - content_other; - } - case PrincipalInfo::TExpandedPrincipalInfo: - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::expanded; - default: - return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::other; - } -} +constexpr nsLiteralCString kAccessError("The operation is insecure"); } // namespace @@ -112,11 +71,12 @@ struct IDBFactory::PendingRequestInfo { } }; -IDBFactory::IDBFactory(const IDBFactoryGuard&) +IDBFactory::IDBFactory(const IDBFactoryGuard&, bool aAllowed) : mBackgroundActor(nullptr), mInnerWindowID(0), mActiveTransactionCount(0), mActiveDatabaseCount(0), + mAllowed(aAllowed), mBackgroundActorFailed(false), mPrivateBrowsingMode(false) { AssertIsOnOwningThread(); @@ -140,16 +100,21 @@ Result, nsresult> IDBFactory::CreateForWindow( nsCOMPtr principal; nsresult rv = AllowedForWindowInternal(aWindow, &principal); - if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) { - NS_WARNING("IndexedDB is not permitted in a third-party window."); - return RefPtr{}; - } - if (NS_WARN_IF(NS_FAILED(rv))) { + if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) { + NS_WARNING("IndexedDB is not permitted in a third-party window."); + } + if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) { IDB_REPORT_INTERNAL_ERR(); } - return Err(rv); + + auto factory = + MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ false); + factory->BindToOwner(aWindow->AsGlobal()); + factory->mInnerWindowID = aWindow->WindowID(); + + return factory; } MOZ_ASSERT(principal); @@ -172,7 +137,7 @@ Result, nsresult> IDBFactory::CreateForWindow( nsCOMPtr webNav = do_GetInterface(aWindow); nsCOMPtr loadContext = do_QueryInterface(webNav); - auto factory = MakeRefPtr(IDBFactoryGuard{}); + auto factory = MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ true); factory->mPrincipalInfo = std::move(principalInfo); factory->BindToOwner(aWindow->AsGlobal()); @@ -203,7 +168,11 @@ Result, nsresult> IDBFactory::CreateForMainThreadJS( MOZ_ASSERT(principal); bool isSystem; if (!AllowedForPrincipal(principal, &isSystem)) { - return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + auto factory = + MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ false); + factory->BindToOwner(aGlobal); + + return factory; } nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo.get()); @@ -220,14 +189,23 @@ Result, nsresult> IDBFactory::CreateForMainThreadJS( // static Result, nsresult> IDBFactory::CreateForWorker( - nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo, + nsIGlobalObject* aGlobal, UniquePtr&& aPrincipalInfo, uint64_t aInnerWindowID) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aGlobal); - MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None); - return CreateInternal(aGlobal, MakeUnique(aPrincipalInfo), - aInnerWindowID); + if (!aPrincipalInfo) { + auto factory = + MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ false); + factory->BindToOwner(aGlobal); + factory->mInnerWindowID = aInnerWindowID; + + return factory; + } + + MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None); + + return CreateInternal(aGlobal, std::move(aPrincipalInfo), aInnerWindowID); } // static @@ -264,10 +242,16 @@ Result, nsresult> IDBFactory::CreateInternal( if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo && aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) { NS_WARNING("IndexedDB not allowed for this principal!"); - return RefPtr{}; + + auto factory = + MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ false); + factory->BindToOwner(aGlobal); + factory->mInnerWindowID = aInnerWindowID; + + return factory; } - auto factory = MakeRefPtr(IDBFactoryGuard{}); + auto factory = MakeRefPtr(IDBFactoryGuard{}, /* aAllowed */ true); factory->mPrincipalInfo = std::move(aPrincipalInfo); factory->BindToOwner(aGlobal); factory->mEventTarget = GetCurrentSerialEventTarget(); @@ -407,6 +391,7 @@ PersistenceType IDBFactory::GetPersistenceType( void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) { AssertIsOnOwningThread(); + MOZ_ASSERT(mAllowed); MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveTransactionCount + aDelta) < mActiveTransactionCount); mActiveTransactionCount += aDelta; @@ -414,6 +399,7 @@ void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) { void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) { AssertIsOnOwningThread(); + MOZ_ASSERT(mAllowed); MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveDatabaseCount + aDelta) < mActiveDatabaseCount); mActiveDatabaseCount += aDelta; @@ -424,6 +410,10 @@ void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) { } bool IDBFactory::IsChrome() const { + if (!mAllowed) { + return false; // Minimal privileges + } + AssertIsOnOwningThread(); MOZ_ASSERT(mPrincipalInfo); @@ -432,39 +422,28 @@ bool IDBFactory::IsChrome() const { RefPtr IDBFactory::Open(JSContext* aCx, const nsAString& aName, - uint64_t aVersion, + const Optional& aVersion, CallerType aCallerType, ErrorResult& aRv) { - return OpenInternal(aCx, - /* aPrincipal */ nullptr, aName, - Optional(aVersion), - /* aDeleting */ false, aCallerType, aRv); -} - -RefPtr IDBFactory::Open(JSContext* aCx, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - CallerType aCallerType, - ErrorResult& aRv) { - // This overload is nonstandard, see bug 1275496. - // Ignore calls with empty options for telemetry of usage count. - // Unfortunately, we cannot distinguish between the use of the method with - // only a single argument (which actually is a standard overload we don't want - // to count) an empty dictionary passed explicitly (which is the custom - // overload we would like to count). However, we assume that the latter is so - // rare that it can be neglected. - if (aOptions.IsAnyMemberPresent()) { - Telemetry::AccumulateCategorical(IdentifyPrincipalType(*mPrincipalInfo)); + if (!mAllowed) { + aRv.ThrowSecurityError(kAccessError); + return nullptr; } return OpenInternal(aCx, - /* aPrincipal */ nullptr, aName, aOptions.mVersion, + /* aPrincipal */ nullptr, aName, aVersion, /* aDeleting */ false, aCallerType, aRv); } -RefPtr IDBFactory::DeleteDatabase( - JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions, - CallerType aCallerType, ErrorResult& aRv) { +RefPtr IDBFactory::DeleteDatabase(JSContext* aCx, + const nsAString& aName, + CallerType aCallerType, + ErrorResult& aRv) { + if (!mAllowed) { + aRv.ThrowSecurityError(kAccessError); + return nullptr; + } + return OpenInternal(aCx, /* aPrincipal */ nullptr, aName, Optional(), /* aDeleting */ true, aCallerType, aRv); @@ -478,6 +457,10 @@ already_AddRefed IDBFactory::Databases(JSContext* const aCx, } RefPtr promise = Promise::CreateInfallible(GetOwnerGlobal()); + if (!mAllowed) { + promise->MaybeRejectWithSecurityError(kAccessError); + return promise.forget(); + } // Nothing can be done here if we have previously failed to create a // background actor. @@ -569,6 +552,11 @@ int16_t IDBFactory::Cmp(JSContext* aCx, JS::Handle aFirst, RefPtr IDBFactory::OpenForPrincipal( JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, uint64_t aVersion, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) { + if (!mAllowed) { + aRv.ThrowSecurityError(kAccessError); + return nullptr; + } + MOZ_ASSERT(aPrincipal); if (!NS_IsMainThread()) { MOZ_CRASH( @@ -584,6 +572,11 @@ RefPtr IDBFactory::OpenForPrincipal( JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) { + if (!mAllowed) { + aRv.ThrowSecurityError(kAccessError); + return nullptr; + } + MOZ_ASSERT(aPrincipal); if (!NS_IsMainThread()) { MOZ_CRASH( @@ -599,6 +592,11 @@ RefPtr IDBFactory::DeleteForPrincipal( JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) { + if (!mAllowed) { + aRv.ThrowSecurityError(kAccessError); + return nullptr; + } + MOZ_ASSERT(aPrincipal); if (!NS_IsMainThread()) { MOZ_CRASH( @@ -611,6 +609,8 @@ RefPtr IDBFactory::DeleteForPrincipal( } nsresult IDBFactory::EnsureBackgroundActor() { + MOZ_ASSERT(mAllowed); + if (mBackgroundActor) { return NS_OK; } @@ -671,6 +671,8 @@ RefPtr IDBFactory::OpenInternal( JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName, const Optional& aVersion, bool aDeleting, CallerType aCallerType, ErrorResult& aRv) { + MOZ_ASSERT(mAllowed); + if (NS_WARN_IF(!GetOwnerGlobal())) { aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return nullptr; @@ -801,6 +803,7 @@ RefPtr IDBFactory::OpenInternal( nsresult IDBFactory::InitiateRequest( const NotNull>& aRequest, const FactoryRequestParams& aParams) { + MOZ_ASSERT(mAllowed); MOZ_ASSERT(mBackgroundActor); MOZ_ASSERT(!mBackgroundActorFailed); diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index d64d571a05..08663eaec4 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -77,11 +77,16 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache { uint32_t mActiveTransactionCount; uint32_t mActiveDatabaseCount; + // When mAllowed is false we throw security errors on all operations. This is + // because although we make storage access decisions when we create the + // IDBFactory, the spec (and content) expects us to only throw if an attempt + // is made to use the resulting IDBFactory. + bool mAllowed; bool mBackgroundActorFailed; bool mPrivateBrowsingMode; public: - explicit IDBFactory(const IDBFactoryGuard&); + IDBFactory(const IDBFactoryGuard&, bool aAllowed); static Result, nsresult> CreateForWindow( nsPIDOMWindowInner* aWindow); @@ -89,8 +94,9 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache { static Result, nsresult> CreateForMainThreadJS( nsIGlobalObject* aGlobal); + // mAllowed shall be false for null aPrincipalInfo. static Result, nsresult> CreateForWorker( - nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo, + nsIGlobalObject* aGlobal, UniquePtr&& aPrincipalInfo, uint64_t aInnerWindowID); static bool AllowedForWindow(nsPIDOMWindowInner* aWindow); @@ -146,21 +152,15 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache { bool IsChrome() const; - [[nodiscard]] RefPtr Open(JSContext* aCx, - const nsAString& aName, - uint64_t aVersion, - CallerType aCallerType, - ErrorResult& aRv); - - [[nodiscard]] RefPtr Open(JSContext* aCx, - const nsAString& aName, - const IDBOpenDBOptions& aOptions, - CallerType aCallerType, - ErrorResult& aRv); + [[nodiscard]] RefPtr Open( + JSContext* aCx, const nsAString& aName, + const Optional& aVersion, CallerType aCallerType, + ErrorResult& aRv); - [[nodiscard]] RefPtr DeleteDatabase( - JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions, - CallerType aCallerType, ErrorResult& aRv); + [[nodiscard]] RefPtr DeleteDatabase(JSContext* aCx, + const nsAString& aName, + CallerType aCallerType, + ErrorResult& aRv); already_AddRefed Databases(JSContext* aCx, ErrorResult& aRv); diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 728f10b105..401c9defb2 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -276,12 +276,22 @@ bool CopyingStructuredCloneWriteCallback(JSContext* aCx, aObj); } +void StructuredCloneErrorCallback(JSContext* aCx, uint32_t aErrorId, + void* aClosure, const char* aErrorMessage) { + // This callback is only used to prevent the default cloning TypeErrors + // from being thrown, as we will throw DataCloneErrors instead per spec. +} + nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) { static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = { - nullptr /* read */, StructuredCloneWriteCallback /* write */, - nullptr /* reportError */, nullptr /* readTransfer */, - nullptr /* writeTransfer */, nullptr /* freeTransfer */, - nullptr /* canTransfer */, nullptr /* sabCloned */ + nullptr /* read */, + StructuredCloneWriteCallback /* write */, + StructuredCloneErrorCallback /* reportError */, + nullptr /* readTransfer */, + nullptr /* writeTransfer */, + nullptr /* freeTransfer */, + nullptr /* canTransfer */, + nullptr /* sabCloned */ }; MOZ_ASSERT(aCx); @@ -555,7 +565,7 @@ bool IDBObjectStore::DeserializeValue( static const JSStructuredCloneCallbacks callbacks = { StructuredCloneReadCallback, nullptr, - nullptr, + StructuredCloneErrorCallback, nullptr, nullptr, nullptr, @@ -1751,7 +1761,7 @@ bool IDBObjectStore::ValueWrapper::Clone(JSContext* aCx) { static const JSStructuredCloneCallbacks callbacks = { CopyingStructuredCloneReadCallback /* read */, CopyingStructuredCloneWriteCallback /* write */, - nullptr /* reportError */, + StructuredCloneErrorCallback /* reportError */, nullptr /* readTransfer */, nullptr /* writeTransfer */, nullptr /* freeTransfer */, diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index 7e0c607617..a6223bfc95 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -16,6 +16,7 @@ #include "js/MemoryFunctions.h" #include "js/Object.h" // JS::GetBuiltinClass #include "js/PropertyAndElement.h" // JS_DefineElement, JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_HasOwnPropertyById +#include "js/SharedArrayBuffer.h" // IsSharedArrayBufferObject #include "js/Value.h" #include "jsfriendapi.h" #include "mozilla/Casting.h" @@ -102,6 +103,84 @@ IDBResult ConvertArrayValueToKey( aPolicy.EndSubkeyList(); return Ok(); } + +bool IsDetachedBuffer(JSContext* aCx, JS::Handle aJsBufferSource) { + if (JS_IsArrayBufferViewObject(aJsBufferSource)) { + bool unused = false; + JS::Rooted viewedArrayBuffer( + aCx, JS_GetArrayBufferViewBuffer(aCx, aJsBufferSource, &unused)); + return JS::IsDetachedArrayBufferObject(viewedArrayBuffer); + } + + return JS::IsDetachedArrayBufferObject(aJsBufferSource); +} + +// To get a copy of the bytes held by the buffer source given a buffer source +// type instance bufferSource: +IDBResult, IDBSpecialValue::Invalid> +GetByteSpanFromJSBufferSource(JSContext* aCx, + JS::Handle aJsBufferSource) { + // 1. Let jsBufferSource be the result of converting bufferSource to a + // JavaScript value. + + // 2. Let jsArrayBuffer be jsBufferSource. + JS::Handle& jsArrayBuffer = aJsBufferSource; + + // 3. Let offset be 0. + size_t offset = 0u; + + // 4. Let length be 0. + size_t length = 0u; + + // 8. Let bytes be a new byte sequence of length equal to length. + uint8_t* bytes = nullptr; // Note: Copy is deferred, no allocation here + + // 5. If jsBufferSource has a [[ViewedArrayBuffer]] internal slot, then: + if (JS_IsArrayBufferViewObject(aJsBufferSource)) { + // 5.1 Set jsArrayBuffer to jsBufferSource.[[ViewedArrayBuffer]]. + // 5.2 Set offset to jsBufferSource.[[ByteOffset]]. + // 5.3 Set length to jsBufferSource.[[ByteLength]]. + + // 9. For i in the range offset to offset + length − 1, inclusive, set + // bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true, + // Unordered). + (void)offset; + bool unused = false; + if (!JS_GetObjectAsArrayBufferView(jsArrayBuffer, &length, &unused, + &bytes)) { + return Err(IDBError(SpecialValues::Invalid)); + } + + // 6. Otherwise: + } else { + // 6.1 Assert: jsBufferSource is an ArrayBuffer or SharedArrayBuffer object. + MOZ_RELEASE_ASSERT(JS::IsArrayBufferObject(aJsBufferSource) || + JS::IsSharedArrayBufferObject(aJsBufferSource)); + + // 6.2 Set length to jsBufferSource.[[ArrayBufferByteLength]]. + + // 9. For i in the range offset to offset + length − 1, inclusive, set + // bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true, + // Unordered). + (void)offset; + if (!JS::GetObjectAsArrayBuffer(jsArrayBuffer, &length, &bytes)) { + return Err(IDBError(SpecialValues::Invalid)); + } + } + + // 7. If IsDetachedBuffer(jsArrayBuffer) is true, then return the empty byte + // sequence. + if (IsDetachedBuffer(aCx, aJsBufferSource)) { + // Note: As the web platform tests assume, and as has been discussed at + // https://github.com/w3c/IndexedDB/issues/417 - we are better off by + // throwing a DataCloneError. The spec language is about to be revised. + return Err(IDBError(SpecialValues::Invalid)); + } + + // 10. Return bytes. + return Span{bytes, length}.AsConst(); +} + } // namespace /* @@ -435,8 +514,19 @@ IDBResult Key::EncodeJSValInternal( // If `input` is a buffer source type if (JS::IsArrayBufferObject(object) || JS_IsArrayBufferViewObject(object)) { - const bool isViewObject = JS_IsArrayBufferViewObject(object); - return EncodeBinary(object, isViewObject, aTypeOffset); + // 1. Let bytes be the result of getting a copy of the bytes held by the + // buffer source input. + + auto res = GetByteSpanFromJSBufferSource(aCx, object); + + // Rethrow any exceptions. + if (res.isErr()) { + return res.propagateErr(); + } + + // 2. Return a new key with type binary and value bytes. + // Note: The copy takes place here. + return EncodeAsString(res.inspect(), eBinary + aTypeOffset); } // If IsArray(`input`) @@ -518,13 +608,14 @@ nsresult Key::DecodeJSValInternal(const EncodedDataType*& aPos, } else if (*aPos - aTypeOffset == eFloat) { aVal.setDouble(DecodeNumber(aPos, aEnd)); } else if (*aPos - aTypeOffset == eBinary) { - JSObject* binary = DecodeBinary(aPos, aEnd, aCx); - if (!binary) { + JSObject* arrayBufferObject = + GetArrayBufferObjectFromDataRange(aPos, aEnd, aCx); + if (!arrayBufferObject) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - aVal.setObject(*binary); + aVal.setObject(*arrayBufferObject); } else { MOZ_ASSERT_UNREACHABLE("Unknown key type!"); } @@ -869,28 +960,10 @@ double Key::DecodeNumber(const EncodedDataType*& aPos, return BitwiseCast(bits); } -Result Key::EncodeBinary(JSObject* aObject, bool aIsViewObject, - uint8_t aTypeOffset) { - uint8_t* bufferData; - size_t bufferLength; - - // We must use JS::GetObjectAsArrayBuffer()/JS_GetObjectAsArrayBufferView() - // instead of js::GetArrayBufferLengthAndData(). The object might be wrapped, - // the former will handle the wrapped case, the later won't. - if (aIsViewObject) { - bool unused; - JS_GetObjectAsArrayBufferView(aObject, &bufferLength, &unused, &bufferData); - } else { - JS::GetObjectAsArrayBuffer(aObject, &bufferLength, &bufferData); - } - - return EncodeAsString(Span{bufferData, bufferLength}.AsConst(), - eBinary + aTypeOffset); -} - // static -JSObject* Key::DecodeBinary(const EncodedDataType*& aPos, - const EncodedDataType* aEnd, JSContext* aCx) { +JSObject* Key::GetArrayBufferObjectFromDataRange(const EncodedDataType*& aPos, + const EncodedDataType* aEnd, + JSContext* aCx) { JS::Rooted rv(aCx); DecodeStringy( aPos, aEnd, diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h index 25ddd3e0b1..f4e6570a96 100644 --- a/dom/indexedDB/Key.h +++ b/dom/indexedDB/Key.h @@ -226,9 +226,6 @@ class Key { Result EncodeNumber(double aFloat, uint8_t aType); - Result EncodeBinary(JSObject* aObject, bool aIsViewObject, - uint8_t aTypeOffset); - // Decoding functions. aPos points into mBuffer and is adjusted to point // past the consumed value. (Note: this may be beyond aEnd). static nsresult DecodeJSVal(const EncodedDataType*& aPos, @@ -241,8 +238,9 @@ class Key { static double DecodeNumber(const EncodedDataType*& aPos, const EncodedDataType* aEnd); - static JSObject* DecodeBinary(const EncodedDataType*& aPos, - const EncodedDataType* aEnd, JSContext* aCx); + static JSObject* GetArrayBufferObjectFromDataRange( + const EncodedDataType*& aPos, const EncodedDataType* aEnd, + JSContext* aCx); // Returns the size of the decoded data for stringy (string or binary), // excluding a null terminator. diff --git a/dom/indexedDB/crashtests/1499854-1.html b/dom/indexedDB/crashtests/1499854-1.html index 0e10601134..7d3a86a62f 100644 --- a/dom/indexedDB/crashtests/1499854-1.html +++ b/dom/indexedDB/crashtests/1499854-1.html @@ -6,7 +6,7 @@ o1 = new Int32Array(51488) o2 = new ArrayBuffer(13964) for (let i = 0; i < 51488; i++) o1[i] = 0x41 - const dbRequest = window.indexedDB.open('', {}) + const dbRequest = window.indexedDB.open('') dbRequest.onupgradeneeded = function(event) { const store = event.target.result.createObjectStore('IDBStore_0', {}) store.add({}, 'ObjectKey_0') diff --git a/dom/indexedDB/crashtests/1857979-1.html b/dom/indexedDB/crashtests/1857979-1.html index d97aa24562..be5a3c947c 100644 --- a/dom/indexedDB/crashtests/1857979-1.html +++ b/dom/indexedDB/crashtests/1857979-1.html @@ -1,10 +1,10 @@