Fix a crash in IndexedDB.

This commit is contained in:
wolfbeast 2019-09-04 21:04:07 +02:00 • committed by Roy Tam
commit cfc50f18c4
10 changed files with 102 additions and 39 deletions

View file

@ -430,7 +430,7 @@ IDBCursor::Continue(JSContext* aCx,
} }
Key key; Key key;
aRv = key.SetFromJSVal(aCx, aKey); aRv = key.SetFromJSVal(aCx, aKey, /* aCallGetters */ true);
if (aRv.Failed()) { if (aRv.Failed()) {
return; return;
} }
@ -536,7 +536,7 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx,
} }
Key key; Key key;
aRv = key.SetFromJSVal(aCx, aKey); aRv = key.SetFromJSVal(aCx, aKey, /* aCallGetters */ true);
if (aRv.Failed()) { if (aRv.Failed()) {
return; return;
} }
@ -558,7 +558,7 @@ IDBCursor::ContinuePrimaryKey(JSContext* aCx,
} }
Key primaryKey; Key primaryKey;
aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey); aRv = primaryKey.SetFromJSVal(aCx, aPrimaryKey, /* aCallGetters */ true);
if (aRv.Failed()) { if (aRv.Failed()) {
return; return;
} }
@ -718,7 +718,7 @@ IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
const KeyPath& keyPath = objectStore->GetKeyPath(); const KeyPath& keyPath = objectStore->GetKeyPath();
Key key; Key key;
aRv = keyPath.ExtractKey(aCx, aValue, key); aRv = keyPath.ExtractKey(aCx, aValue, key, /* aCallGetters */ false);
if (aRv.Failed()) { if (aRv.Failed()) {
return nullptr; return nullptr;
} }

View file

@ -482,13 +482,13 @@ IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
JS::Handle<JS::Value> aSecond, ErrorResult& aRv) JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
{ {
Key first, second; Key first, second;
nsresult rv = first.SetFromJSVal(aCx, aFirst); nsresult rv = first.SetFromJSVal(aCx, aFirst, /* aCallGetters */ true);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
aRv.Throw(rv); aRv.Throw(rv);
return 0; return 0;
} }
rv = second.SetFromJSVal(aCx, aSecond); rv = second.SetFromJSVal(aCx, aSecond, /* aCallGetters */ true);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
aRv.Throw(rv); aRv.Throw(rv);
return 0; return 0;

View file

@ -24,7 +24,7 @@ GetKeyFromJSVal(JSContext* aCx,
JS::Handle<JS::Value> aVal, JS::Handle<JS::Value> aVal,
Key& aKey) Key& aKey)
{ {
nsresult rv = aKey.SetFromJSVal(aCx, aVal); nsresult rv = aKey.SetFromJSVal(aCx, aVal, /* aCallGetters */ true);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB); MOZ_ASSERT(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
return rv; return rv;

View file

@ -1084,7 +1084,7 @@ IDBObjectStore::AppendIndexUpdateInfo(
if (!aMultiEntry) { if (!aMultiEntry) {
Key key; Key key;
rv = aKeyPath.ExtractKey(aCx, aVal, key); rv = aKeyPath.ExtractKey(aCx, aVal, key, /* aCallGetters */ false);
// If an index's keyPath doesn't match an object, we ignore that object. // If an index's keyPath doesn't match an object, we ignore that object.
if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) { if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) {
@ -1128,13 +1128,13 @@ IDBObjectStore::AppendIndexUpdateInfo(
for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
JS::Rooted<JS::Value> arrayItem(aCx); JS::Rooted<JS::Value> arrayItem(aCx);
if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) { if (NS_WARN_IF(!JS_GetOwnElement(aCx, array, arrayIndex, &arrayItem))) {
IDB_REPORT_INTERNAL_ERR(); IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
} }
Key value; Key value;
if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) || if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem, /* aCallGetters */ false)) ||
value.IsUnset()) { value.IsUnset()) {
// Not a value we can do anything with, ignore it. // Not a value we can do anything with, ignore it.
continue; continue;
@ -1153,7 +1153,7 @@ IDBObjectStore::AppendIndexUpdateInfo(
} }
else { else {
Key value; Key value;
if (NS_FAILED(value.SetFromJSVal(aCx, val)) || if (NS_FAILED(value.SetFromJSVal(aCx, val, /* aCallGetters */ false)) ||
value.IsUnset()) { value.IsUnset()) {
// Not a value we can do anything with, ignore it. // Not a value we can do anything with, ignore it.
return NS_OK; return NS_OK;
@ -1324,12 +1324,12 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
if (!HasValidKeyPath()) { if (!HasValidKeyPath()) {
// Out-of-line keys must be passed in. // Out-of-line keys must be passed in.
rv = aKey.SetFromJSVal(aCx, aKeyVal); rv = aKey.SetFromJSVal(aCx, aKeyVal, /* aCallGetters */ true);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
} else if (!isAutoIncrement) { } else if (!isAutoIncrement) {
rv = GetKeyPath().ExtractKey(aCx, aValue, aKey); rv = GetKeyPath().ExtractKey(aCx, aValue, aKey, /* aCallGetters */ false);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -1368,7 +1368,8 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
aValue, aValue,
aKey, aKey,
&GetAddInfoCallback, &GetAddInfoCallback,
&data); &data,
/* aCallGetters */ false);
} else { } else {
rv = GetAddInfoCallback(aCx, &data); rv = GetAddInfoCallback(aCx, &data);
} }

View file

@ -201,8 +201,11 @@ Key::ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const
} }
nsresult nsresult
Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal, Key::EncodeJSValInternal(JSContext* aCx,
uint8_t aTypeOffset, uint16_t aRecursionDepth) JS::Handle<JS::Value> aVal,
uint8_t aTypeOffset,
uint16_t aRecursionDepth,
bool aCallGetters)
{ {
static_assert(eMaxType * kMaxArrayCollapse < 256, static_assert(eMaxType * kMaxArrayCollapse < 256,
"Unable to encode jsvals."); "Unable to encode jsvals.");
@ -257,13 +260,18 @@ Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
for (uint32_t index = 0; index < length; index++) { for (uint32_t index = 0; index < length; index++) {
JS::Rooted<JS::Value> val(aCx); JS::Rooted<JS::Value> val(aCx);
if (!JS_GetElement(aCx, obj, index, &val)) { bool ok = aCallGetters ? JS_GetElement(aCx, obj, index, &val)
: JS_GetOwnElement(aCx, obj, index, &val);
if (!ok) {
IDB_REPORT_INTERNAL_ERR(); IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
} }
nsresult rv = EncodeJSValInternal(aCx, val, aTypeOffset, nsresult rv = EncodeJSValInternal(aCx,
aRecursionDepth + 1); val,
aTypeOffset,
aRecursionDepth + 1,
aCallGetters);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -406,9 +414,10 @@ Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
nsresult nsresult
Key::EncodeJSVal(JSContext* aCx, Key::EncodeJSVal(JSContext* aCx,
JS::Handle<JS::Value> aVal, JS::Handle<JS::Value> aVal,
uint8_t aTypeOffset) uint8_t aTypeOffset,
bool aCallGetters)
{ {
return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0, aCallGetters);
} }
void void
@ -741,7 +750,8 @@ Key::SetFromValueArray(mozIStorageValueArray* aValues,
nsresult nsresult
Key::SetFromJSVal(JSContext* aCx, Key::SetFromJSVal(JSContext* aCx,
JS::Handle<JS::Value> aVal) JS::Handle<JS::Value> aVal,
bool aCallGetters)
{ {
mBuffer.Truncate(); mBuffer.Truncate();
@ -750,7 +760,7 @@ Key::SetFromJSVal(JSContext* aCx,
return NS_OK; return NS_OK;
} }
nsresult rv = EncodeJSVal(aCx, aVal, 0); nsresult rv = EncodeJSVal(aCx, aVal, 0, aCallGetters);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
Unset(); Unset();
return rv; return rv;
@ -793,9 +803,15 @@ Key::ToJSVal(JSContext* aCx,
} }
nsresult nsresult
Key::AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal) Key::AppendItem(JSContext* aCx,
bool aFirstOfArray,
JS::Handle<JS::Value> aVal,
bool aCallGetters)
{ {
nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); nsresult rv = EncodeJSVal(aCx,
aVal,
aFirstOfArray ? eMaxType : 0,
aCallGetters);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
Unset(); Unset();
return rv; return rv;

View file

@ -203,7 +203,7 @@ public:
} }
nsresult nsresult
SetFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal); SetFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, bool aCallGetters);
nsresult nsresult
ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) const; ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) const;
@ -212,7 +212,10 @@ public:
ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aVal) const; ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aVal) const;
nsresult nsresult
AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal); AppendItem(JSContext* aCx,
bool aFirstOfArray,
JS::Handle<JS::Value> aVal,
bool aCallGetters);
nsresult nsresult
ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const; ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const;
@ -283,7 +286,10 @@ private:
// Encoding functions. These append the encoded value to the end of mBuffer // Encoding functions. These append the encoded value to the end of mBuffer
nsresult nsresult
EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset); EncodeJSVal(JSContext* aCx,
JS::Handle<JS::Value> aVal,
uint8_t aTypeOffset,
bool aCallGetters);
void void
EncodeString(const nsAString& aString, uint8_t aTypeOffset); EncodeString(const nsAString& aString, uint8_t aTypeOffset);
@ -331,7 +337,8 @@ private:
EncodeJSValInternal(JSContext* aCx, EncodeJSValInternal(JSContext* aCx,
JS::Handle<JS::Value> aVal, JS::Handle<JS::Value> aVal,
uint8_t aTypeOffset, uint8_t aTypeOffset,
uint16_t aRecursionDepth); uint16_t aRecursionDepth,
bool aCallGetters);
static nsresult static nsresult
DecodeJSValInternal(const unsigned char*& aPos, DecodeJSValInternal(const unsigned char*& aPos,

View file

@ -372,11 +372,13 @@ KeyPath::AppendStringWithValidation(const nsAString& aString)
} }
nsresult nsresult
KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const KeyPath::ExtractKey(JSContext* aCx,
const JS::Value& aValue,
Key& aKey,
bool aCallGetters) const
{ {
uint32_t len = mStrings.Length(); uint32_t len = mStrings.Length();
JS::Rooted<JS::Value> value(aCx); JS::Rooted<JS::Value> value(aCx);
aKey.Unset(); aKey.Unset();
for (uint32_t i = 0; i < len; ++i) { for (uint32_t i = 0; i < len; ++i) {
@ -388,7 +390,10 @@ KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const
return rv; return rv;
} }
if (NS_FAILED(aKey.AppendItem(aCx, IsArray() && i == 0, value))) { if (NS_FAILED(aKey.AppendItem(aCx,
IsArray() && i == 0,
value,
aCallGetters))) {
NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset"); NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset");
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR; return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
} }
@ -437,9 +442,12 @@ KeyPath::ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue,
} }
nsresult nsresult
KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, KeyPath::ExtractOrCreateKey(JSContext* aCx,
Key& aKey, ExtractOrCreateKeyCallback aCallback, const JS::Value& aValue,
void* aClosure) const Key& aKey,
ExtractOrCreateKeyCallback aCallback,
void* aClosure,
bool aCallGetters) const
{ {
NS_ASSERTION(IsString(), "This doesn't make sense!"); NS_ASSERTION(IsString(), "This doesn't make sense!");
@ -455,7 +463,7 @@ KeyPath::ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue,
return rv; return rv;
} }
if (NS_FAILED(aKey.AppendItem(aCx, false, value))) { if (NS_FAILED(aKey.AppendItem(aCx, false, value, aCallGetters))) {
NS_ASSERTION(aKey.IsUnset(), "Should be unset"); NS_ASSERTION(aKey.IsUnset(), "Should be unset");
return value.isUndefined() ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR; return value.isUndefined() ? NS_OK : NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
} }

View file

@ -72,7 +72,10 @@ public:
Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath); Parse(const Nullable<OwningStringOrStringSequence>& aValue, KeyPath* aKeyPath);
nsresult nsresult
ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const; ExtractKey(JSContext* aCx,
const JS::Value& aValue,
Key& aKey,
bool aCallGetters) const;
nsresult nsresult
ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue, ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue,
@ -82,9 +85,12 @@ public:
(*ExtractOrCreateKeyCallback)(JSContext* aCx, void* aClosure); (*ExtractOrCreateKeyCallback)(JSContext* aCx, void* aClosure);
nsresult nsresult
ExtractOrCreateKey(JSContext* aCx, const JS::Value& aValue, Key& aKey, ExtractOrCreateKey(JSContext* aCx,
const JS::Value& aValue,
Key& aKey,
ExtractOrCreateKeyCallback aCallback, ExtractOrCreateKeyCallback aCallback,
void* aClosure) const; void* aClosure,
bool aCallGetters) const;
inline bool IsValid() const { inline bool IsValid() const {
return mType != NONEXISTENT; return mType != NONEXISTENT;

View file

@ -2010,6 +2010,28 @@ JS_GetOwnUCPropertyDescriptor(JSContext* cx, HandleObject obj, const char16_t* n
return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc); return JS_GetOwnPropertyDescriptorById(cx, obj, id, desc);
} }
JS_PUBLIC_API(bool)
JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp)
{
RootedId id(cx);
if (!IndexToId(cx, index, &id)) {
return false;
}
Rooted<PropertyDescriptor> desc(cx);
if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
return false;
}
if (desc.object() && desc.isDataDescriptor()) {
vp.set(desc.value());
} else {
vp.setUndefined();
}
return true;
}
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id, JS_GetPropertyDescriptorById(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc) MutableHandle<PropertyDescriptor> desc)

View file

@ -2922,6 +2922,9 @@ extern JS_PUBLIC_API(bool)
JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, JS_GetOwnUCPropertyDescriptor(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
JS::MutableHandle<JS::PropertyDescriptor> desc); JS::MutableHandle<JS::PropertyDescriptor> desc);
extern JS_PUBLIC_API(bool)
JS_GetOwnElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::MutableHandleValue vp);
/** /**
* Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
* if no own property is found directly on obj. The object on which the * if no own property is found directly on obj. The object on which the