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 488053 cb4aa4bcb7fa800d8aed782c4e5dec55a694cfe0
parent 488052 55c19b23576a09a2735c1f6cfb1f61acd61ab470
child 488054 60633a71c04da76b15117f771b88b4a6ec76617d
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewerstcampbell
bugs1492678
milestone64.0a1
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());
         }