Xdr.cpp (5353B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "vm/Xdr.h" 8 9 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF 10 #include "mozilla/EndianUtils.h" // mozilla::NativeEndian, MOZ_LITTLE_ENDIAN 11 #include "mozilla/Result.h" // mozilla::{Result, Ok, Err}, MOZ_TRY 12 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 13 14 #include <algorithm> // std::transform 15 #include <stddef.h> // size_t 16 #include <stdint.h> // uint8_t, uint32_t, uintptr_t 17 #include <string> // std::char_traits 18 #include <type_traits> // std::is_same_v 19 #include <utility> // std::move 20 21 #include "frontend/FrontendContext.h" // FrontendContext 22 #include "js/Transcoding.h" // JS::TranscodeResult, JS::TranscodeBuffer, JS::TranscodeRange 23 #include "js/UniquePtr.h" // UniquePtr 24 #include "js/Utility.h" // JS::FreePolicy, js_delete 25 #include "vm/JSContext.h" // JSContext, ReportAllocationOverflow 26 #include "vm/StringType.h" // JSString 27 28 using namespace js; 29 30 using mozilla::Utf8Unit; 31 32 #ifdef DEBUG 33 bool XDRCoderBase::validateResultCode(FrontendContext* fc, 34 JS::TranscodeResult code) const { 35 return fc->hadErrors() == bool(code == JS::TranscodeResult::Throw); 36 } 37 #endif 38 39 template <XDRMode mode> 40 XDRResult XDRState<mode>::codeChars(char* chars, size_t nchars) { 41 return codeBytes(chars, nchars); 42 } 43 44 template <XDRMode mode> 45 XDRResult XDRState<mode>::codeChars(Latin1Char* chars, size_t nchars) { 46 static_assert(sizeof(Latin1Char) == 1, 47 "Latin1Char must be 1 byte for nchars below to be the " 48 "proper count of bytes"); 49 static_assert(std::is_same_v<Latin1Char, unsigned char>, 50 "Latin1Char must be unsigned char to C++-safely reinterpret " 51 "the bytes generically copied below as Latin1Char"); 52 return codeBytes(chars, nchars); 53 } 54 55 template <XDRMode mode> 56 XDRResult XDRState<mode>::codeChars(Utf8Unit* units, size_t count) { 57 if (count == 0) { 58 return Ok(); 59 } 60 61 if (mode == XDR_ENCODE) { 62 uint8_t* ptr = buf->write(count); 63 if (!ptr) { 64 return fail(JS::TranscodeResult::Throw); 65 } 66 67 std::transform(units, units + count, ptr, 68 [](const Utf8Unit& unit) { return unit.toUint8(); }); 69 } else { 70 const uint8_t* ptr = buf->read(count); 71 if (!ptr) { 72 return fail(JS::TranscodeResult::Failure_BadDecode); 73 } 74 75 std::transform(ptr, ptr + count, units, 76 [](const uint8_t& value) { return Utf8Unit(value); }); 77 } 78 79 return Ok(); 80 } 81 82 template <XDRMode mode> 83 XDRResult XDRState<mode>::codeChars(char16_t* chars, size_t nchars) { 84 if (nchars == 0) { 85 return Ok(); 86 } 87 88 size_t nbytes = nchars * sizeof(char16_t); 89 if (mode == XDR_ENCODE) { 90 uint8_t* ptr = buf->write(nbytes); 91 if (!ptr) { 92 return fail(JS::TranscodeResult::Throw); 93 } 94 95 // |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|. 96 mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars); 97 } else { 98 const uint8_t* ptr = buf->read(nbytes); 99 if (!ptr) { 100 return fail(JS::TranscodeResult::Failure_BadDecode); 101 } 102 103 // |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|. 104 mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars); 105 } 106 return Ok(); 107 } 108 109 template <XDRMode mode, typename CharT> 110 static XDRResult XDRCodeCharsZ(XDRState<mode>* xdr, 111 XDRTranscodeString<CharT>& buffer) { 112 MOZ_ASSERT_IF(mode == XDR_ENCODE, !buffer.empty()); 113 MOZ_ASSERT_IF(mode == XDR_DECODE, buffer.empty()); 114 115 using OwnedString = js::UniquePtr<CharT[], JS::FreePolicy>; 116 OwnedString owned; 117 118 static_assert(JSString::MAX_LENGTH <= INT32_MAX, 119 "String length must fit in int32_t"); 120 121 uint32_t length = 0; 122 CharT* chars = nullptr; 123 124 if (mode == XDR_ENCODE) { 125 chars = const_cast<CharT*>(buffer.template ref<const CharT*>()); 126 127 // Set a reasonable limit on string length. 128 size_t lengthSizeT = std::char_traits<CharT>::length(chars); 129 if (lengthSizeT > JSString::MAX_LENGTH) { 130 ReportAllocationOverflow(xdr->fc()); 131 return xdr->fail(JS::TranscodeResult::Throw); 132 } 133 length = static_cast<uint32_t>(lengthSizeT); 134 } 135 MOZ_TRY(xdr->codeUint32(&length)); 136 137 if (mode == XDR_DECODE) { 138 owned = 139 xdr->fc()->getAllocator()->template make_pod_array<CharT>(length + 1); 140 if (!owned) { 141 return xdr->fail(JS::TranscodeResult::Throw); 142 } 143 chars = owned.get(); 144 } 145 146 MOZ_TRY(xdr->codeChars(chars, length)); 147 if (mode == XDR_DECODE) { 148 // Null-terminate and transfer ownership to caller. 149 owned[length] = '\0'; 150 buffer.template construct<OwnedString>(std::move(owned)); 151 } 152 153 return Ok(); 154 } 155 156 template <XDRMode mode> 157 XDRResult XDRState<mode>::codeCharsZ(XDRTranscodeString<char>& buffer) { 158 return XDRCodeCharsZ(this, buffer); 159 } 160 161 template <XDRMode mode> 162 XDRResult XDRState<mode>::codeCharsZ(XDRTranscodeString<char16_t>& buffer) { 163 return XDRCodeCharsZ(this, buffer); 164 } 165 166 template class js::XDRState<XDR_ENCODE>; 167 template class js::XDRState<XDR_DECODE>;