Bug 1371484 - Fix Key::EncodeAsString(). r=baku
☠☠ backed out by 8adfbfd01ccd ☠ ☠
authorBevis Tseng <btseng@mozilla.com>
Mon, 19 Jun 2017 19:03:06 +0800
changeset 419710 810a0598980114e79c968546b24d75337f83c79b
parent 419709 710b97157320ea30bb87a1cc3845e2d6e80fbb83
child 419711 8adfbfd01ccd2500c243fd4e47cf08ecb9f237c2
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1371484
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1371484 - Fix Key::EncodeAsString(). r=baku MozReview-Commit-ID: F29YzvgresY
dom/indexedDB/Key.cpp
dom/indexedDB/Key.h
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -3,21 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "Key.h"
 
 #include <algorithm>
+#include <stdint.h> // for UINT32_MAX, uintptr_t
 #include "IndexedDatabaseManager.h"
 #include "js/Date.h"
 #include "js/Value.h"
 #include "jsfriendapi.h"
 #include "mozilla/Casting.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageValueArray.h"
 #include "nsAlgorithm.h"
 #include "nsJSUtils.h"
 #include "ReportInternalError.h"
 #include "xpcpublic.h"
@@ -191,17 +193,20 @@ Key::ToLocaleBasedKey(Key& aTarget, cons
       }
     } else {
       // Decode string and reencode
       uint8_t typeOffset = *it - eString;
       MOZ_ASSERT((typeOffset % eArray == 0) && (typeOffset / eArray <= 2));
 
       nsDependentString str;
       DecodeString(it, end, str);
-      aTarget.EncodeLocaleString(str, typeOffset, aLocale);
+      nsresult rv = aTarget.EncodeLocaleString(str, typeOffset, aLocale);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
   aTarget.TrimBuffer();
   return NS_OK;
 }
 #endif
 
 nsresult
@@ -216,18 +221,17 @@ Key::EncodeJSValInternal(JSContext* aCx,
   }
 
   if (aVal.isString()) {
     nsAutoJSString str;
     if (!str.init(aCx, aVal)) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
-    EncodeString(str, aTypeOffset);
-    return NS_OK;
+    return EncodeString(str, aTypeOffset);
   }
 
   if (aVal.isNumber()) {
     double d = aVal.toNumber();
     if (mozilla::IsNaN(d)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
     EncodeNumber(d, eFloat + aTypeOffset);
@@ -294,23 +298,21 @@ Key::EncodeJSValInternal(JSContext* aCx,
         IDB_REPORT_INTERNAL_ERR();
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
       EncodeNumber(t, eDate + aTypeOffset);
       return NS_OK;
     }
 
     if (JS_IsArrayBufferObject(obj)) {
-      EncodeBinary(obj, /* aIsViewObject */ false, aTypeOffset);
-      return NS_OK;
+      return EncodeBinary(obj, /* aIsViewObject */ false, aTypeOffset);
     }
 
     if (JS_IsArrayBufferViewObject(obj)) {
-      EncodeBinary(obj, /* aIsViewObject */ true, aTypeOffset);
-      return NS_OK;
+      return EncodeBinary(obj, /* aIsViewObject */ true, aTypeOffset);
     }
   }
 
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
@@ -410,54 +412,72 @@ Key::DecodeJSValInternal(const unsigned 
 nsresult
 Key::EncodeJSVal(JSContext* aCx,
                  JS::Handle<JS::Value> aVal,
                  uint8_t aTypeOffset)
 {
   return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0);
 }
 
-void
+nsresult
 Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset)
 {
   const char16_t* start = aString.BeginReading();
   const char16_t* end = aString.EndReading();
-  EncodeString(start, end, aTypeOffset);
+  return EncodeString(start, end, aTypeOffset);
+}
+
+template <typename T>
+nsresult
+Key::EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset)
+{
+  return EncodeAsString(aStart, aEnd, eString + aTypeOffset);
 }
 
 template <typename T>
-void
-Key::EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset)
-{
-  EncodeAsString(aStart, aEnd, eString + aTypeOffset);
-}
-
-template <typename T>
-void
+nsresult
 Key::EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType)
 {
   // First measure how long the encoded string will be.
+  if (NS_WARN_IF(aStart > aEnd || UINT32_MAX - 2 < uintptr_t(aEnd - aStart))) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
 
   // The +2 is for initial 3 and trailing 0. We'll compensate for multi-byte
   // chars below.
-  uint32_t size = (aEnd - aStart) + 2;
+  CheckedUint32 size = (aEnd - aStart) + 2;
+
+  MOZ_ASSERT(size.isValid());
 
   const T* start = aStart;
   const T* end = aEnd;
   for (const T* iter = start; iter < end; ++iter) {
     if (*iter > ONE_BYTE_LIMIT) {
       size += char16_t(*iter) > TWO_BYTE_LIMIT ? 2 : 1;
+      if (!size.isValid()) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
     }
   }
 
   // Allocate memory for the new size
   uint32_t oldLen = mBuffer.Length();
+  size += oldLen;
+
+  if (!size.isValid()) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
   char* buffer;
-  if (!mBuffer.GetMutableData(&buffer, oldLen + size)) {
-    return;
+  if (!mBuffer.GetMutableData(&buffer, size.value())) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
   buffer += oldLen;
 
   // Write type marker
   *(buffer++) = aType;
 
   // Encode string
   for (const T* iter = start; iter < end; ++iter) {
@@ -476,16 +496,18 @@ Key::EncodeAsString(const T* aStart, con
       *(buffer++) = (char)c;
     }
   }
 
   // Write end marker
   *(buffer++) = eTerminator;
 
   NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes");
+
+  return NS_OK;
 }
 
 #ifdef ENABLE_INTL_API
 nsresult
 Key::EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
                         const nsCString& aLocale)
 {
   const int length = aString.Length();
@@ -512,20 +534,19 @@ Key::EncodeLocaleString(const nsDependen
                                     sortKeyLength);
   }
 
   ucol_close(collator);
   if (NS_WARN_IF(sortKeyLength == 0)) {
     return NS_ERROR_FAILURE;
   }
 
-  EncodeString(keyBuffer.Elements(),
-               keyBuffer.Elements()+sortKeyLength,
-               aTypeOffset);
-  return NS_OK;
+  return EncodeString(keyBuffer.Elements(),
+                      keyBuffer.Elements()+sortKeyLength,
+                      aTypeOffset);
 }
 #endif
 
 // static
 nsresult
 Key::DecodeJSVal(const unsigned char*& aPos,
                  const unsigned char* aEnd,
                  JSContext* aCx,
@@ -635,30 +656,30 @@ Key::DecodeNumber(const unsigned char*& 
   // Note: The subtraction from 0 below is necessary to fix
   // MSVC build warning C4146 (negating an unsigned value).
   const uint64_t signbit = FloatingPoint<double>::kSignBit;
   uint64_t bits = number & signbit ? (number & ~signbit) : (0 - number);
 
   return BitwiseCast<double>(bits);
 }
 
-void
+nsresult
 Key::EncodeBinary(JSObject* aObject, bool aIsViewObject, uint8_t aTypeOffset)
 {
   uint8_t* bufferData;
   uint32_t bufferLength;
   bool unused;
 
   if (aIsViewObject) {
     js::GetArrayBufferViewLengthAndData(aObject, &bufferLength, &unused, &bufferData);
   } else {
     js::GetArrayBufferLengthAndData(aObject, &bufferLength, &unused, &bufferData);
   }
 
-  EncodeAsString(bufferData, bufferData + bufferLength, eBinary + aTypeOffset);
+  return EncodeAsString(bufferData, bufferData + bufferLength, eBinary + aTypeOffset);
 }
 
 // static
 JSObject*
 Key::DecodeBinary(const unsigned char*& aPos,
                   const unsigned char* aEnd,
                   JSContext* aCx)
 {
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -282,37 +282,37 @@ private:
 
     mBuffer.Truncate(end + 1 - mBuffer.BeginReading());
   }
 
   // Encoding functions. These append the encoded value to the end of mBuffer
   nsresult
   EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset);
 
-  void
+  nsresult
   EncodeString(const nsAString& aString, uint8_t aTypeOffset);
 
   template <typename T>
-  void
+  nsresult
   EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset);
 
   template <typename T>
-  void
+  nsresult
   EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType);
 
 #ifdef ENABLE_INTL_API
   nsresult
   EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
                      const nsCString& aLocale);
 #endif
 
   void
   EncodeNumber(double aFloat, uint8_t aType);
 
-  void
+  nsresult
   EncodeBinary(JSObject* aObject, bool aIsViewObject, uint8_t aTypeOffset);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
   // past the consumed value.
   static nsresult
   DecodeJSVal(const unsigned char*& aPos,
               const unsigned char* aEnd,
               JSContext* aCx,