Bug 1371484 - Fix Key::EncodeAsString(). r=baku
authorBevis Tseng <btseng@mozilla.com>
Mon, 19 Jun 2017 19:03:06 +0800
changeset 422388 301acb1080e7ad0312000b3ced6c272d2b715916
parent 422387 68a7ead060bfd1fa8d453286362a7377d9887f42
child 422389 a06c1daddacd43ac6dd084e1c4110ac26e7ae262
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [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,74 @@ 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;
+  uint32_t checkedSize = aEnd - aStart;
+  CheckedUint32 size = checkedSize;
+  size += 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 +498,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 +536,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 +658,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,