Bug 1052579 - Change all found JSString allocation sites to new arena r=sfink
authorChris Martin <cmartin@mozilla.com>
Wed, 24 Apr 2019 13:57:07 +0000
changeset 470655 e51a022e039fce5f3e30079d6700a7575913cfbe
parent 470654 69e2758dbbad1423d57f8067499786451d9695b3
child 470656 1e92c0bda3d99e4fa004a2329821288bb0b6e233
push id35910
push usercbrindusan@mozilla.com
push dateWed, 24 Apr 2019 21:51:39 +0000
treeherdermozilla-central@b9a625eff9e3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1052579
milestone68.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 1052579 - Change all found JSString allocation sites to new arena r=sfink Differential Revision: https://phabricator.services.mozilla.com/D25710
js/public/Utility.h
js/src/builtin/String.cpp
js/src/builtin/TestingFunctions.cpp
js/src/ctypes/CTypes.cpp
js/src/jsapi.cpp
js/src/jsutil.cpp
js/src/util/StringBuffer.cpp
js/src/util/StringBuffer.h
js/src/vm/CharacterEncoding.cpp
js/src/vm/Compartment.cpp
js/src/vm/InlineCharBuffer-inl.h
js/src/vm/JSAtom.cpp
js/src/vm/StringType-inl.h
js/src/vm/StringType.cpp
js/src/vm/StringType.h
js/src/wasm/WasmJS.cpp
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -346,20 +346,25 @@ struct MOZ_RAII JS_PUBLIC_DATA AutoEnter
 } /* namespace js */
 
 // Malloc allocation.
 
 namespace js {
 
 extern JS_PUBLIC_DATA arena_id_t MallocArena;
 extern JS_PUBLIC_DATA arena_id_t ArrayBufferContentsArena;
+extern JS_PUBLIC_DATA arena_id_t StringBufferArena;
 
 extern void InitMallocAllocator();
 extern void ShutDownMallocAllocator();
 
+#  ifdef MOZ_DEBUG
+extern void AssertJSStringBufferInCorrectArena(const void* ptr);
+#  endif
+
 } /* namespace js */
 
 static inline void* js_arena_malloc(arena_id_t arena, size_t bytes) {
   JS_OOM_POSSIBLY_FAIL();
   return moz_arena_malloc(arena, bytes);
 }
 
 static inline void* js_malloc(size_t bytes) {
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -3622,17 +3622,18 @@ bool js::str_fromCodePoint(JSContext* cx
   }
 
   // Steps 1-2 (omitted).
 
   // Step 3.
   static_assert(
       ARGS_LENGTH_MAX < std::numeric_limits<decltype(args.length())>::max() / 2,
       "|args.length() * 2 + 1| does not overflow");
-  auto elements = cx->make_pod_array<char16_t>(args.length() * 2 + 1);
+  auto elements = cx->make_pod_array<char16_t>(args.length() * 2 + 1,
+                                               js::StringBufferArena);
   if (!elements) {
     return false;
   }
 
   // Steps 4-5.
   unsigned length = 0;
   for (unsigned nextIndex = 0; nextIndex < args.length(); nextIndex++) {
     // Steps 5.a-d.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2490,17 +2490,17 @@ static bool ReadGeckoProfilingStack(JSCo
         case JS::ProfilingFrameIterator::Frame_Wasm:
           frameKindStr = "wasm";
           break;
         default:
           frameKindStr = "unknown";
       }
 
       UniqueChars label =
-          DuplicateStringToArena(js::MallocArena, cx, frames[i].label);
+          DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
       if (!label) {
         return false;
       }
 
       if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
         return false;
       }
     }
@@ -3553,17 +3553,17 @@ struct FindPathHandler {
     // nothing to be done on subsequent visits.
     if (!first) {
       return true;
     }
 
     // Record how we reached this node. This is the last edge on a
     // shortest path to this node.
     EdgeName edgeName =
-        DuplicateStringToArena(js::MallocArena, cx, edge.name.get());
+        DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
     if (!edgeName) {
       return false;
     }
     *backEdge = BackEdge(origin, std::move(edgeName));
 
     // Have we reached our final target node?
     if (edge.referent == target) {
       // Record the path that got us here, which must be a shortest path.
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -7776,31 +7776,31 @@ static bool ReadStringCommon(JSContext* 
   }
 
   args.rval().setString(result);
   return true;
 }
 
 bool CData::ReadString(JSContext* cx, unsigned argc, Value* vp) {
   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
-                          "CData.prototype.readString", js::MallocArena);
+                          "CData.prototype.readString", js::StringBufferArena);
 }
 
 bool CDataFinalizer::Methods::ReadString(JSContext* cx, unsigned argc,
                                          Value* vp) {
   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp,
                           "CDataFinalizer.prototype.readString",
-                          js::MallocArena);
+                          js::StringBufferArena);
 }
 
 bool CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc,
                                        Value* vp) {
   return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp,
                           "CData.prototype.readStringReplaceMalformed",
-                          js::MallocArena);
+                          js::StringBufferArena);
 }
 
 JSString* CData::GetSourceString(JSContext* cx, HandleObject typeObj,
                                  void* data) {
   // Walk the types, building up the toSource() string.
   // First, we build up the type expression:
   // 't.ptr' for pointers;
   // 't.array([n])' for arrays;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1131,25 +1131,25 @@ JS_PUBLIC_API void* JS_realloc(JSContext
 }
 
 JS_PUBLIC_API void JS_free(JSContext* cx, void* p) { return js_free(p); }
 
 JS_PUBLIC_API void* JS_string_malloc(JSContext* cx, size_t nbytes) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   return static_cast<void*>(
-      cx->maybe_pod_malloc<uint8_t>(nbytes, js::MallocArena));
+      cx->maybe_pod_malloc<uint8_t>(nbytes, js::StringBufferArena));
 }
 
 JS_PUBLIC_API void* JS_string_realloc(JSContext* cx, void* p, size_t oldBytes,
                                       size_t newBytes) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   return static_cast<void*>(cx->maybe_pod_realloc<uint8_t>(
-      static_cast<uint8_t*>(p), oldBytes, newBytes, js::MallocArena));
+      static_cast<uint8_t*>(p), oldBytes, newBytes, js::StringBufferArena));
 }
 
 JS_PUBLIC_API void JS_string_free(JSContext* cx, void* p) { return js_free(p); }
 
 JS_PUBLIC_API void JS_freeop(JSFreeOp* fop, void* p) {
   return FreeOp::get(fop)->free_(p);
 }
 
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -78,28 +78,46 @@ void FailureSimulator::reset() {
 }  // namespace oom
 }  // namespace js
 #endif  // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 
 bool js::gDisablePoisoning = false;
 
 JS_PUBLIC_DATA arena_id_t js::MallocArena;
 JS_PUBLIC_DATA arena_id_t js::ArrayBufferContentsArena;
+JS_PUBLIC_DATA arena_id_t js::StringBufferArena;
 
 void js::InitMallocAllocator() {
   MallocArena = moz_create_arena();
   ArrayBufferContentsArena = moz_create_arena();
+  StringBufferArena = moz_create_arena();
 }
 
 void js::ShutDownMallocAllocator() {
   // Until Bug 1364359 is fixed it is unsafe to call moz_dispose_arena.
   // moz_dispose_arena(MallocArena);
   // moz_dispose_arena(ArrayBufferContentsArena);
 }
 
+#ifdef MOZ_DEBUG
+extern void js::AssertJSStringBufferInCorrectArena(const void* ptr) {
+//  `jemalloc_ptr_info()` only exists if MOZ_MEMORY is defined, and it only
+//  returns an arenaId if MOZ_DEBUG is defined. Otherwise, this function is
+//  a no-op.
+#  if defined(MOZ_MEMORY) && defined(MOZ_DEBUG)
+  if (ptr) {
+    jemalloc_ptr_info_t ptrInfo{};
+    jemalloc_ptr_info(ptr, &ptrInfo);
+    MOZ_ASSERT(ptrInfo.tag != TagUnknown);
+    MOZ_ASSERT(ptrInfo.arenaId == js::StringBufferArena);
+  }
+#  endif
+}
+#endif
+
 JS_PUBLIC_API void JS_Assert(const char* s, const char* file, int ln) {
   MOZ_ReportAssertionFailure(s, file, ln);
   MOZ_CRASH();
 }
 
 #ifdef __linux__
 
 #  include <malloc.h>
--- a/js/src/util/StringBuffer.cpp
+++ b/js/src/util/StringBuffer.cpp
@@ -45,17 +45,17 @@ char16_t* StringBuffer::stealChars() {
   }
 
   return ExtractWellSized<char16_t>(twoByteChars());
 }
 
 bool StringBuffer::inflateChars() {
   MOZ_ASSERT(isLatin1());
 
-  TwoByteCharBuffer twoByte(cx);
+  TwoByteCharBuffer twoByte(TempAllocPolicy{cx_, arenaId_});
 
   /*
    * Note: we don't use Vector::capacity() because it always returns a
    * value >= sInlineCapacity. Since Latin1CharBuffer::sInlineCapacity >
    * TwoByteCharBuffer::sInlineCapacitychars, we'd always malloc here.
    */
   size_t capacity = Max(reserved_, latin1Chars().length());
   if (!twoByte.reserve(capacity)) {
@@ -101,45 +101,45 @@ JSFlatString* StringBuffer::finishString
   cx->updateMallocCounter(sizeof(CharT) * len);
 
   return str;
 }
 
 JSFlatString* JSStringBuilder::finishString() {
   size_t len = length();
   if (len == 0) {
-    return cx->names().empty;
+    return cx_->names().empty;
   }
 
-  if (!JSString::validateLength(cx, len)) {
+  if (!JSString::validateLength(cx_, len)) {
     return nullptr;
   }
 
   JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_TWO_BYTE <
                    TwoByteCharBuffer::InlineLength);
   JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_LATIN1 <
                    Latin1CharBuffer::InlineLength);
 
-  return isLatin1() ? finishStringInternal<Latin1Char>(cx)
-                    : finishStringInternal<char16_t>(cx);
+  return isLatin1() ? finishStringInternal<Latin1Char>(cx_)
+                    : finishStringInternal<char16_t>(cx_);
 }
 
 JSAtom* StringBuffer::finishAtom() {
   size_t len = length();
   if (len == 0) {
-    return cx->names().empty;
+    return cx_->names().empty;
   }
 
   if (isLatin1()) {
-    JSAtom* atom = AtomizeChars(cx, latin1Chars().begin(), len);
+    JSAtom* atom = AtomizeChars(cx_, latin1Chars().begin(), len);
     latin1Chars().clear();
     return atom;
   }
 
-  JSAtom* atom = AtomizeChars(cx, twoByteChars().begin(), len);
+  JSAtom* atom = AtomizeChars(cx_, twoByteChars().begin(), len);
   twoByteChars().clear();
   return atom;
 }
 
 bool js::ValueToStringBufferSlow(JSContext* cx, const Value& arg,
                                  StringBuffer& sb) {
   RootedValue v(cx, arg);
   if (!ToPrimitive(cx, JSTYPE_STRING, &v)) {
--- a/js/src/util/StringBuffer.h
+++ b/js/src/util/StringBuffer.h
@@ -34,17 +34,18 @@ class StringBuffer {
 
   /*
    * The Vector's buffer may be either stolen or copied, so we need to use
    * TempAllocPolicy and account for the memory manually when stealing.
    */
   using Latin1CharBuffer = BufferType<Latin1Char>;
   using TwoByteCharBuffer = BufferType<char16_t>;
 
-  JSContext* cx;
+  JSContext* cx_;
+  const arena_id_t& arenaId_;
 
   /*
    * If Latin1 strings are enabled, cb starts out as a Latin1CharBuffer. When
    * a TwoByte char is appended, inflateChars() constructs a TwoByteCharBuffer
    * and copies the Latin1 chars.
    */
   mozilla::MaybeOneOf<Latin1CharBuffer, TwoByteCharBuffer> cb;
 
@@ -92,18 +93,20 @@ class StringBuffer {
   }
 
   MOZ_MUST_USE bool inflateChars();
 
   template <typename CharT>
   JSFlatString* finishStringInternal(JSContext* cx);
 
  public:
-  explicit StringBuffer(JSContext* cx) : cx(cx), reserved_(0) {
-    cb.construct<Latin1CharBuffer>(cx);
+  explicit StringBuffer(JSContext* cx,
+                        const arena_id_t& arenaId = js::MallocArena)
+      : cx_(cx), arenaId_(arenaId), reserved_(0) {
+    cb.construct<Latin1CharBuffer>(TempAllocPolicy{cx_, arenaId_});
   }
 
   void clear() {
     if (isLatin1()) {
       latin1Chars().clear();
     } else {
       twoByteChars().clear();
     }
@@ -273,17 +276,18 @@ class StringBuffer {
    * exactly the characters in this buffer (inflated to TwoByte), it is *not*
    * null-terminated unless the last appended character was '\0'.
    */
   char16_t* stealChars();
 };
 
 class JSStringBuilder : public StringBuffer {
  public:
-  explicit JSStringBuilder(JSContext* cx) : StringBuffer(cx) {}
+  explicit JSStringBuilder(JSContext* cx)
+      : StringBuffer(cx, js::StringBufferArena) {}
 
   /*
    * Creates a string from the characters in this buffer, then (regardless
    * whether string creation succeeded or failed) empties the buffer.
    */
   JSFlatString* finishString();
 };
 
@@ -352,26 +356,26 @@ inline bool StringBuffer::appendSubstrin
   }
   return base->hasLatin1Chars()
              ? twoByteChars().append(base->latin1Chars(nogc) + off, len)
              : twoByteChars().append(base->twoByteChars(nogc) + off, len);
 }
 
 inline bool StringBuffer::appendSubstring(JSString* base, size_t off,
                                           size_t len) {
-  JSLinearString* linear = base->ensureLinear(cx);
+  JSLinearString* linear = base->ensureLinear(cx_);
   if (!linear) {
     return false;
   }
 
   return appendSubstring(linear, off, len);
 }
 
 inline bool StringBuffer::append(JSString* str) {
-  JSLinearString* linear = str->ensureLinear(cx);
+  JSLinearString* linear = str->ensureLinear(cx_);
   if (!linear) {
     return false;
   }
 
   return append(linear);
 }
 
 /* ES5 9.8 ToString, appending the result to the string buffer. */
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -697,17 +697,17 @@ bool StringBuffer::append(const Utf8Unit
 
   // Determine how many UTF-16 code units are required to represent the
   // remaining units.
   size_t utf16Len = 0;
   auto countInflated = [&utf16Len](char16_t c) -> LoopDisposition {
     utf16Len++;
     return LoopDisposition::Continue;
   };
-  if (!InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx, remainingUtf8,
+  if (!InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx_, remainingUtf8,
                                               countInflated)) {
     return false;
   }
 
   TwoByteCharBuffer& buf = twoByteChars();
 
   size_t i = buf.length();
   if (!buf.growByUninitialized(utf16Len)) {
@@ -718,12 +718,12 @@ bool StringBuffer::append(const Utf8Unit
 
   char16_t* toFill = &buf[i];
   auto appendUtf16 = [&toFill](char16_t unit) {
     *toFill++ = unit;
     return LoopDisposition::Continue;
   };
 
   MOZ_ALWAYS_TRUE(
-      InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx, remainingUtf8, appendUtf16));
+      InflateUTF8ToUTF16<OnUTF8Error::Throw>(cx_, remainingUtf8, appendUtf16));
   MOZ_ASSERT(toFill == buf.end());
   return true;
 }
--- a/js/src/vm/Compartment.cpp
+++ b/js/src/vm/Compartment.cpp
@@ -107,26 +107,26 @@ static JSString* CopyStringPure(JSContex
     return chars.isLatin1() ? NewStringCopyN<CanGC>(
                                   cx, chars.latin1Range().begin().get(), len)
                             : NewStringCopyNDontDeflate<CanGC>(
                                   cx, chars.twoByteRange().begin().get(), len);
   }
 
   if (str->hasLatin1Chars()) {
     UniquePtr<Latin1Char[], JS::FreePolicy> copiedChars =
-        str->asRope().copyLatin1CharsZ(cx, js::MallocArena);
+        str->asRope().copyLatin1CharsZ(cx, js::StringBufferArena);
     if (!copiedChars) {
       return nullptr;
     }
 
     return NewString<CanGC>(cx, std::move(copiedChars), len);
   }
 
   UniqueTwoByteChars copiedChars =
-      str->asRope().copyTwoByteCharsZ(cx, js::MallocArena);
+      str->asRope().copyTwoByteCharsZ(cx, js::StringBufferArena);
   if (!copiedChars) {
     return nullptr;
   }
 
   return NewStringDontDeflate<CanGC>(cx, std::move(copiedChars), len);
 }
 
 bool Compartment::wrap(JSContext* cx, MutableHandleString strp) {
--- a/js/src/vm/InlineCharBuffer-inl.h
+++ b/js/src/vm/InlineCharBuffer-inl.h
@@ -69,40 +69,42 @@ class MOZ_NON_PARAM InlineCharBuffer {
   bool maybeAlloc(JSContext* cx, size_t length) {
     assertValidRequest(0, length);
 
     if (length <= InlineCapacity) {
       return true;
     }
 
     MOZ_ASSERT(!heapStorage, "heap storage already allocated");
-    heapStorage = cx->make_pod_array<CharT>(length + 1);
+    heapStorage = cx->make_pod_array<CharT>(length + 1, js::StringBufferArena);
     return !!heapStorage;
   }
 
   bool maybeRealloc(JSContext* cx, size_t oldLength, size_t newLength) {
     assertValidRequest(oldLength, newLength);
 
     if (newLength <= InlineCapacity) {
       return true;
     }
 
     if (!heapStorage) {
-      heapStorage = cx->make_pod_array<CharT>(newLength + 1);
+      heapStorage =
+          cx->make_pod_array<CharT>(newLength + 1, js::StringBufferArena);
       if (!heapStorage) {
         return false;
       }
 
       MOZ_ASSERT(oldLength <= InlineCapacity);
       mozilla::PodCopy(heapStorage.get(), inlineStorage, oldLength);
       return true;
     }
 
     CharT* oldChars = heapStorage.release();
-    CharT* newChars = cx->pod_realloc(oldChars, oldLength + 1, newLength + 1);
+    CharT* newChars = cx->pod_realloc(oldChars, oldLength + 1, newLength + 1,
+                                      js::StringBufferArena);
     if (!newChars) {
       js_free(oldChars);
       return false;
     }
 
     heapStorage.reset(newChars);
     return true;
   }
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -894,17 +894,18 @@ static MOZ_ALWAYS_INLINE JSFlatString* M
                                          chars->encoding);
     return str;
   }
 
   // MakeAtomUTF8Helper is called from deep in the Atomization path, which
   // expects functions to fail gracefully with nullptr on OOM, without throwing.
   //
   // Flat strings are null-terminated. Leave room with length + 1
-  UniquePtr<CharT[], JS::FreePolicy> newStr(js_pod_malloc<CharT>(length + 1));
+  UniquePtr<CharT[], JS::FreePolicy> newStr(
+      js_pod_arena_malloc<CharT>(js::StringBufferArena, length + 1));
   if (!newStr) {
     return nullptr;
   }
 
   InflateUTF8CharsToBufferAndTerminate(chars->utf8, newStr.get(), length,
                                        chars->encoding);
 
   return JSFlatString::new_<NoGC>(cx, std::move(newStr), length);
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -244,22 +244,26 @@ MOZ_ALWAYS_INLINE JSLinearString* JSDepe
   }
   str->init(cx, base, start, length);
   return str;
 }
 
 MOZ_ALWAYS_INLINE void JSFlatString::init(const char16_t* chars,
                                           size_t length) {
   setLengthAndFlags(length, INIT_FLAT_FLAGS);
+  // Check that the new buffer is located in the StringBufferArena
+  checkStringCharsArena(chars);
   d.s.u2.nonInlineCharsTwoByte = chars;
 }
 
 MOZ_ALWAYS_INLINE void JSFlatString::init(const JS::Latin1Char* chars,
                                           size_t length) {
   setLengthAndFlags(length, INIT_FLAT_FLAGS | LATIN1_CHARS_BIT);
+  // Check that the new buffer is located in the StringBufferArena
+  checkStringCharsArena(chars);
   d.s.u2.nonInlineCharsLatin1 = chars;
 }
 
 template <js::AllowGC allowGC, typename CharT>
 MOZ_ALWAYS_INLINE JSFlatString* JSFlatString::new_(
     JSContext* cx, js::UniquePtr<CharT[], JS::FreePolicy> chars,
     size_t length) {
   MOZ_ASSERT(chars[length] == CharT(0));
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -288,17 +288,17 @@ static MOZ_ALWAYS_INLINE bool AllocChars
   static const size_t DOUBLING_MAX = 1024 * 1024;
   numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8)
                                      : RoundUpPow2(numChars);
 
   /* Like length, capacity does not include the null char, so take it out. */
   *capacity = numChars - 1;
 
   JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(CharT) < UINT32_MAX);
-  *chars = str->zone()->pod_malloc<CharT>(numChars);
+  *chars = str->zone()->pod_malloc<CharT>(numChars, js::StringBufferArena);
   return *chars != nullptr;
 }
 
 UniqueLatin1Chars JSRope::copyLatin1CharsZ(JSContext* maybecx,
                                            arena_id_t destArenaId) const {
   return copyCharsInternal<Latin1Char>(maybecx, true, destArenaId);
 }
 
@@ -856,17 +856,17 @@ template <typename Dest, typename Src,
 static void FillFromCompatibleAndTerminate(Dest* dest, Src* src,
                                            size_t length) {
   FillFromCompatibleAndTerminate(dest, const_cast<const Src*>(src), length);
 }
 
 template <typename CharT>
 JSFlatString* JSDependentString::undependInternal(JSContext* cx) {
   size_t n = length();
-  auto s = cx->make_pod_array<CharT>(n + 1);
+  auto s = cx->make_pod_array<CharT>(n + 1, js::StringBufferArena);
   if (!s) {
     return nullptr;
   }
 
   if (!isTenured()) {
     if (!cx->runtime()->gc.nursery().registerMallocedBuffer(s.get())) {
       ReportOutOfMemory(cx);
       return nullptr;
@@ -1440,17 +1440,17 @@ JSFlatString* JSString::ensureFlat(JSCon
   }
   return asExternal().ensureFlat(cx);
 }
 
 JSFlatString* JSExternalString::ensureFlat(JSContext* cx) {
   MOZ_ASSERT(hasTwoByteChars());
 
   size_t n = length();
-  auto s = cx->make_pod_array<char16_t>(n + 1);
+  auto s = cx->make_pod_array<char16_t>(n + 1, js::StringBufferArena);
   if (!s) {
     return nullptr;
   }
 
   if (!isTenured()) {
     if (!cx->runtime()->gc.nursery().registerMallocedBuffer(s.get())) {
       ReportOutOfMemory(cx);
       return nullptr;
@@ -1575,17 +1575,17 @@ static JSFlatString* NewStringDeflated(J
     return str;
   }
 
   if (JSInlineString::lengthFits<Latin1Char>(n)) {
     return NewInlineStringDeflated<allowGC>(
         cx, mozilla::Range<const char16_t>(s, n));
   }
 
-  auto news = cx->make_pod_array<Latin1Char>(n + 1);
+  auto news = cx->make_pod_array<Latin1Char>(n + 1, js::StringBufferArena);
   if (!news) {
     return nullptr;
   }
 
   MOZ_ASSERT(CanStoreCharsAsLatin1(s, n));
   FillFromCompatibleAndTerminate(news.get(), s, n);
 
   return JSFlatString::new_<allowGC>(cx, std::move(news), n);
@@ -1607,17 +1607,17 @@ static JSFlatString* NewStringDeflatedFr
     if (!str) {
       return nullptr;
     }
 
     FillFromCompatibleAndTerminate(storage, chars, length);
     return str;
   }
 
-  auto news = cx->make_pod_array<Latin1Char>(length + 1);
+  auto news = cx->make_pod_array<Latin1Char>(length + 1, js::StringBufferArena);
   if (!news) {
     cx->recoverFromOutOfMemory();
     return nullptr;
   }
 
   FillFromCompatibleAndTerminate(news.get(), chars, length);
 
   return JSFlatString::new_<NoGC>(cx, std::move(news), length);
@@ -1736,17 +1736,17 @@ JSFlatString* NewStringCopyNDontDeflate(
   if (JSFlatString* str = TryEmptyOrStaticString(cx, s, n)) {
     return str;
   }
 
   if (JSInlineString::lengthFits<CharT>(n)) {
     return NewInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n));
   }
 
-  auto news = cx->make_pod_array<CharT>(n + 1);
+  auto news = cx->make_pod_array<CharT>(n + 1, js::StringBufferArena);
   if (!news) {
     if (!allowGC) {
       cx->recoverFromOutOfMemory();
     }
     return nullptr;
   }
 
   FillAndTerminate(news.get(), s, n);
@@ -1778,17 +1778,17 @@ static JSFlatString* NewUndeflatedString
     if (!str) {
       return nullptr;
     }
 
     FillAndTerminate(storage, chars, length);
     return str;
   }
 
-  auto news = cx->make_pod_array<char16_t>(length + 1);
+  auto news = cx->make_pod_array<char16_t>(length + 1, js::StringBufferArena);
   if (!news) {
     cx->recoverFromOutOfMemory();
     return nullptr;
   }
 
   FillAndTerminate(news.get(), chars, length);
 
   return JSFlatString::new_<NoGC>(cx, std::move(news), length);
@@ -1840,28 +1840,30 @@ JSFlatString* NewStringCopyUTF8N(JSConte
   JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
   if (encoding == JS::SmallestEncoding::ASCII) {
     return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
   }
 
   size_t length;
   if (encoding == JS::SmallestEncoding::Latin1) {
     UniqueLatin1Chars latin1(
-        UTF8CharsToNewLatin1CharsZ(cx, utf8, &length, js::MallocArena).get());
+        UTF8CharsToNewLatin1CharsZ(cx, utf8, &length, js::StringBufferArena)
+            .get());
     if (!latin1) {
       return nullptr;
     }
 
     return NewString<allowGC>(cx, std::move(latin1), length);
   }
 
   MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16);
 
   UniqueTwoByteChars utf16(
-      UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length, js::MallocArena).get());
+      UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length, js::StringBufferArena)
+          .get());
   if (!utf16) {
     return nullptr;
   }
 
   return NewString<allowGC>(cx, std::move(utf16), length);
 }
 
 template JSFlatString* NewStringCopyUTF8N<CanGC>(JSContext* cx,
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -393,16 +393,23 @@ class JSString : public js::gc::Cell {
 
  protected:
   template <typename CharT>
   MOZ_ALWAYS_INLINE void setNonInlineChars(const CharT* chars);
 
   MOZ_ALWAYS_INLINE
   uint32_t flags() const { return uint32_t(d.flags_); }
 
+  template <typename CharT>
+  static MOZ_ALWAYS_INLINE void checkStringCharsArena(const CharT* chars) {
+#ifdef MOZ_DEBUG
+    js::AssertJSStringBufferInCorrectArena(chars);
+#endif
+  }
+
  public:
   MOZ_ALWAYS_INLINE
   size_t length() const {
 #if JS_BITS_PER_WORD == 32
     return d.length_;
 #else
     return uint32_t(d.flags_ >> 32);
 #endif
@@ -1902,22 +1909,26 @@ MOZ_ALWAYS_INLINE bool JSInlineString::l
 template <>
 MOZ_ALWAYS_INLINE bool JSInlineString::lengthFits<char16_t>(size_t length) {
   // If it fits in a fat inline string, it fits in any inline string.
   return JSFatInlineString::lengthFits<char16_t>(length);
 }
 
 template <>
 MOZ_ALWAYS_INLINE void JSString::setNonInlineChars(const char16_t* chars) {
+  // Check that the new buffer is located in the StringBufferArena
+  checkStringCharsArena(chars);
   d.s.u2.nonInlineCharsTwoByte = chars;
 }
 
 template <>
 MOZ_ALWAYS_INLINE void JSString::setNonInlineChars(
     const JS::Latin1Char* chars) {
+  // Check that the new buffer is located in the StringBufferArena
+  checkStringCharsArena(chars);
   d.s.u2.nonInlineCharsLatin1 = chars;
 }
 
 MOZ_ALWAYS_INLINE const JS::Latin1Char* JSLinearString::rawLatin1Chars() const {
   MOZ_ASSERT(JSString::isLinear());
   MOZ_ASSERT(hasLatin1Chars());
   return isInline() ? d.inlineStorageLatin1 : d.s.u2.nonInlineCharsLatin1;
 }
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -2757,17 +2757,18 @@ static bool Reject(JSContext* cx, const 
   // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
   // way to create an ErrorObject for an arbitrary error code with multiple
   // replacements.
   UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
   if (!str) {
     return false;
   }
 
-  RootedString message(cx, NewLatin1StringZ(cx, std::move(str)));
+  size_t len = strlen(str.get());
+  RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len));
   if (!message) {
     return false;
   }
 
   RootedObject errorObj(
       cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, filename, 0,
                               line, 0, nullptr, message));
   if (!errorObj) {