Bug 1492678 - XDR-encode/decode the source text of scripts using codeChars, not codeBytes that can scramble endianness of char16_t source text if the XDR data is read back on a machine of different endianness. r=tcampbell
authorJeff Walden <jwalden@mit.edu>
Tue, 25 Sep 2018 16:30:30 -0400
changeset 439764 cb4aa4bcb7fa800d8aed782c4e5dec55a694cfe0
parent 439763 55c19b23576a09a2735c1f6cfb1f61acd61ab470
child 439765 60633a71c04da76b15117f771b88b4a6ec76617d
push id34787
push usercsabou@mozilla.com
push dateFri, 05 Oct 2018 10:08:34 +0000
treeherdermozilla-central@863c5a0642a8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1492678
milestone64.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 1492678 - XDR-encode/decode the source text of scripts using codeChars, not codeBytes that can scramble endianness of char16_t source text if the XDR data is read back on a machine of different endianness. r=tcampbell
js/src/vm/JSScript.cpp
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -2288,16 +2288,18 @@ ScriptSource::xdrFinalizeEncoder(JS::Tra
 
 template<XDRMode mode>
 XDRResult
 ScriptSource::performXDR(XDRState<mode>* xdr)
 {
     struct CompressedLengthMatcher
     {
         size_t match(Uncompressed&) {
+            // Return 0 for uncompressed source so that |if (compressedLength)|
+            // can be used to distinguish compressed and uncompressed source.
             return 0;
         }
 
         size_t match(Compressed& c) {
             return c.raw.length();
         }
 
         size_t match(BinAST&) {
@@ -2336,60 +2338,84 @@ ScriptSource::performXDR(XDRState<mode>*
     uint8_t hasBinSource = hasBinASTSource();
     MOZ_TRY(xdr->codeUint8(&hasBinSource));
 
     uint8_t retrievable = sourceRetrievable_;
     MOZ_TRY(xdr->codeUint8(&retrievable));
     sourceRetrievable_ = retrievable;
 
     if ((hasSource || hasBinSource) && !sourceRetrievable_) {
-        uint32_t len = 0;
+        uint32_t uncompressedLength = 0;
         if (mode == XDR_ENCODE) {
-            len = length();
+            uncompressedLength = length();
         }
-        MOZ_TRY(xdr->codeUint32(&len));
-
+        MOZ_TRY(xdr->codeUint32(&uncompressedLength));
+
+        // A compressed length of 0 indicates source is uncompressed.
         uint32_t compressedLength;
         if (mode == XDR_ENCODE) {
             CompressedLengthMatcher m;
             compressedLength = data.match(m);
         }
         MOZ_TRY(xdr->codeUint32(&compressedLength));
 
-        size_t byteLen = hasBinSource ? len : compressedLength ? compressedLength : (len * sizeof(char16_t));
         if (mode == XDR_DECODE) {
-            auto bytes = xdr->cx()->template make_pod_array<char>(Max<size_t>(byteLen, 1));
-            if (!bytes) {
-                return xdr->fail(JS::TranscodeResult_Throw);
-            }
-            MOZ_TRY(xdr->codeBytes(bytes.get(), byteLen));
-
             if (hasBinSource) {
 #if defined(JS_BUILD_BINAST)
-                if (!setBinASTSource(xdr->cx(), std::move(bytes), len)) {
+                auto bytes =
+                    xdr->cx()->template make_pod_array<char>(Max<size_t>(uncompressedLength, 1));
+                if (!bytes) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeBytes(bytes.get(), uncompressedLength));
+
+                if (!setBinASTSource(xdr->cx(), std::move(bytes), uncompressedLength)) {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
 #else
                 MOZ_ASSERT(mode != XDR_ENCODE);
                 return xdr->fail(JS::TranscodeResult_Throw);
 #endif /* JS_BUILD_BINAST */
             } else if (compressedLength) {
-                if (!setCompressedSource(xdr->cx(), std::move(bytes), byteLen, len)) {
+                auto bytes =
+                    xdr->cx()->template make_pod_array<char>(Max<size_t>(compressedLength, 1));
+                if (!bytes) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeBytes(bytes.get(), compressedLength));
+
+                if (!setCompressedSource(xdr->cx(), std::move(bytes), compressedLength,
+                                         uncompressedLength))
+                {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
             } else {
-                UniqueTwoByteChars source(reinterpret_cast<char16_t*>(bytes.release()));
-                if (!setSource(xdr->cx(), std::move(source), len)) {
+                auto sourceChars =
+                    xdr->cx()->template make_pod_array<char16_t>(Max<size_t>(uncompressedLength,
+                                                                             1));
+                if (!sourceChars) {
+                    return xdr->fail(JS::TranscodeResult_Throw);
+                }
+                MOZ_TRY(xdr->codeChars(sourceChars.get(), uncompressedLength));
+
+                if (!setSource(xdr->cx(), std::move(sourceChars), uncompressedLength)) {
                     return xdr->fail(JS::TranscodeResult_Throw);
                 }
             }
         } else {
-            RawDataMatcher rdm;
-            void* p = data.match(rdm);
-            MOZ_TRY(xdr->codeBytes(p, byteLen));
+            if (hasBinSource) {
+                void* bytes = data.match(RawDataMatcher());
+                MOZ_TRY(xdr->codeBytes(bytes, uncompressedLength));
+            } else if (compressedLength) {
+                void* bytes = data.match(RawDataMatcher());
+                MOZ_TRY(xdr->codeBytes(bytes, compressedLength));
+            } else {
+                char16_t* sourceChars = static_cast<char16_t*>(data.match(RawDataMatcher()));
+                MOZ_TRY(xdr->codeChars(sourceChars, uncompressedLength));
+            }
         }
 
         uint8_t hasMetadata = !!binASTMetadata_;
         MOZ_TRY(xdr->codeUint8(&hasMetadata));
         if (hasMetadata) {
 #if defined(JS_BUILD_BINAST)
             uint32_t numBinKinds;
             uint32_t numStrings;
@@ -2515,17 +2541,20 @@ ScriptSource::performXDR(XDRState<mode>*
         // Note: If the decoder has an option, then the filename is defined by
         // the CompileOption from the document.
         MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
         if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn)) {
             return xdr->fail(JS::TranscodeResult_Throw);
         }
 
         // Note the content of sources decoded when recording or replaying.
-        if (mode == XDR_DECODE && hasSourceText() && mozilla::recordreplay::IsRecordingOrReplaying()) {
+        if (mode == XDR_DECODE &&
+            hasSourceText() &&
+            mozilla::recordreplay::IsRecordingOrReplaying())
+        {
             UncompressedSourceCache::AutoHoldEntry holder;
             ScriptSource::PinnedChars chars(xdr->cx(), this, holder, 0, length());
             if (!chars.get()) {
                 return xdr->fail(JS::TranscodeResult_Throw);
             }
             mozilla::recordreplay::NoteContentParse(this, filename(), "application/javascript",
                                                     chars.get(), length());
         }