%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/decoder.h |
// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_DECODER_H_ #define V8_WASM_DECODER_H_ #include <cinttypes> #include <cstdarg> #include <memory> #include "src/base/compiler-specific.h" #include "src/base/memory.h" #include "src/base/strings.h" #include "src/base/vector.h" #include "src/flags/flags.h" #include "src/utils/utils.h" #include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-result.h" namespace v8 { namespace internal { namespace wasm { #define TRACE(...) \ do { \ if (v8_flags.trace_wasm_decoder) PrintF(__VA_ARGS__); \ } while (false) #define TRACE_IF(cond, ...) \ do { \ if (v8_flags.trace_wasm_decoder && (cond)) PrintF(__VA_ARGS__); \ } while (false) // A {DecodeResult} only stores the failure / success status, but no data. using DecodeResult = VoidResult; struct WasmFunction; class ITracer { public: static constexpr ITracer* NoTrace = nullptr; // Hooks for extracting byte offsets of things. virtual void TypeOffset(uint32_t offset) = 0; virtual void ImportOffset(uint32_t offset) = 0; virtual void ImportsDone() = 0; virtual void TableOffset(uint32_t offset) = 0; virtual void MemoryOffset(uint32_t offset) = 0; virtual void TagOffset(uint32_t offset) = 0; virtual void GlobalOffset(uint32_t offset) = 0; virtual void StartOffset(uint32_t offset) = 0; virtual void ElementOffset(uint32_t offset) = 0; virtual void DataOffset(uint32_t offset) = 0; virtual void StringOffset(uint32_t offset) = 0; // Hooks for annotated hex dumps. virtual void Bytes(const uint8_t* start, uint32_t count) = 0; virtual void Description(const char* desc) = 0; virtual void Description(const char* desc, size_t length) = 0; virtual void Description(uint32_t number) = 0; virtual void Description(ValueType type) = 0; virtual void Description(HeapType type) = 0; virtual void Description(const FunctionSig* sig) = 0; virtual void NextLine() = 0; virtual void NextLineIfFull() = 0; virtual void NextLineIfNonEmpty() = 0; virtual void InitializerExpression(const uint8_t* start, const uint8_t* end, ValueType expected_type) = 0; virtual void FunctionBody(const WasmFunction* func, const uint8_t* start) = 0; virtual void FunctionName(uint32_t func_index) = 0; virtual void NameSection(const uint8_t* start, const uint8_t* end, uint32_t offset) = 0; virtual ~ITracer() = default; }; // A helper utility to decode bytes, integers, fields, varints, etc, from // a buffer of bytes. class Decoder { public: // Don't run validation, assume valid input. static constexpr struct NoValidationTag { static constexpr bool validate = false; static constexpr bool full_validation = false; } kNoValidation = {}; // Run validation but only store a generic error. static constexpr struct BooleanValidationTag { static constexpr bool validate = true; static constexpr bool full_validation = false; } kBooleanValidation = {}; // Run full validation with error message and location. static constexpr struct FullValidationTag { static constexpr bool validate = true; static constexpr bool full_validation = true; } kFullValidation = {}; struct NoName { constexpr NoName(const char*) {} operator const char*() const { UNREACHABLE(); } }; // Pass a {NoName} if we know statically that we do not use it anyway (we are // not tracing (in release mode) and not running full validation). #ifdef DEBUG template <typename ValidationTag> using Name = const char*; #else template <typename ValidationTag> using Name = std::conditional_t<ValidationTag::full_validation, const char*, NoName>; #endif enum TraceFlag : bool { kTrace = true, kNoTrace = false }; Decoder(const uint8_t* start, const uint8_t* end, uint32_t buffer_offset = 0) : Decoder(start, start, end, buffer_offset) {} explicit Decoder(const base::Vector<const uint8_t> bytes, uint32_t buffer_offset = 0) : Decoder(bytes.begin(), bytes.begin() + bytes.length(), buffer_offset) {} Decoder(const uint8_t* start, const uint8_t* pc, const uint8_t* end, uint32_t buffer_offset = 0) : start_(start), pc_(pc), end_(end), buffer_offset_(buffer_offset) { DCHECK_LE(start, pc); DCHECK_LE(pc, end); DCHECK_EQ(static_cast<uint32_t>(end - start), end - start); } virtual ~Decoder() = default; // Reads an 8-bit unsigned integer. template <typename ValidationTag> uint8_t read_u8(const uint8_t* pc, Name<ValidationTag> msg = "expected 1 byte") { return read_little_endian<uint8_t, ValidationTag>(pc, msg); } // Reads a 16-bit unsigned integer (little endian). template <typename ValidationTag> uint16_t read_u16(const uint8_t* pc, Name<ValidationTag> msg = "expected 2 bytes") { return read_little_endian<uint16_t, ValidationTag>(pc, msg); } // Reads a 32-bit unsigned integer (little endian). template <typename ValidationTag> uint32_t read_u32(const uint8_t* pc, Name<ValidationTag> msg = "expected 4 bytes") { return read_little_endian<uint32_t, ValidationTag>(pc, msg); } // Reads a 64-bit unsigned integer (little endian). template <typename ValidationTag> uint64_t read_u64(const uint8_t* pc, Name<ValidationTag> msg = "expected 8 bytes") { return read_little_endian<uint64_t, ValidationTag>(pc, msg); } // Reads a variable-length unsigned integer (little endian). Returns the read // value and the number of bytes read. template <typename ValidationTag> std::pair<uint32_t, uint32_t> read_u32v(const uint8_t* pc, Name<ValidationTag> name = "LEB32") { return read_leb<uint32_t, ValidationTag, kNoTrace>(pc, name); } // Reads a variable-length signed integer (little endian). Returns the read // value and the number of bytes read. template <typename ValidationTag> std::pair<int32_t, uint32_t> read_i32v( const uint8_t* pc, Name<ValidationTag> name = "signed LEB32") { return read_leb<int32_t, ValidationTag, kNoTrace>(pc, name); } // Reads a variable-length unsigned integer (little endian). Returns the read // value and the number of bytes read. template <typename ValidationTag> std::pair<uint64_t, uint32_t> read_u64v(const uint8_t* pc, Name<ValidationTag> name = "LEB64") { return read_leb<uint64_t, ValidationTag, kNoTrace>(pc, name); } // Reads a variable-length signed integer (little endian). Returns the read // value and the number of bytes read. template <typename ValidationTag> std::pair<int64_t, uint32_t> read_i64v( const uint8_t* pc, Name<ValidationTag> name = "signed LEB64") { return read_leb<int64_t, ValidationTag, kNoTrace>(pc, name); } // Reads a variable-length 33-bit signed integer (little endian). Returns the // read value and the number of bytes read. template <typename ValidationTag> std::pair<int64_t, uint32_t> read_i33v( const uint8_t* pc, Name<ValidationTag> name = "signed LEB33") { return read_leb<int64_t, ValidationTag, kNoTrace, 33>(pc, name); } // Reads a prefixed-opcode, possibly with variable-length index. // Returns the read opcode and the number of bytes that make up this opcode, // *including* the prefix byte. For most opcodes, it will be 2. template <typename ValidationTag> std::pair<WasmOpcode, uint32_t> read_prefixed_opcode( const uint8_t* pc, Name<ValidationTag> name = "prefixed opcode") { // Prefixed opcodes all use LEB128 encoding. auto [index, index_length] = read_u32v<ValidationTag>(pc + 1, "prefixed opcode index"); uint32_t length = index_length + 1; // 1 for prefix byte. // Only support opcodes that go up to 0xFFF (when decoded). Anything // bigger will need more than 2 bytes, and the '<< 12' below will be wrong. if (ValidationTag::validate && V8_UNLIKELY(index > 0xfff)) { errorf(pc, "Invalid prefixed opcode %d", index); // On validation failure we return "unreachable" (opcode 0). static_assert(kExprUnreachable == 0); return {kExprUnreachable, 0}; } if (index > 0xff) { return {static_cast<WasmOpcode>((*pc) << 12 | index), length}; } return {static_cast<WasmOpcode>((*pc) << 8 | index), length}; } // Reads a 8-bit unsigned integer (byte) and advances {pc_}. uint8_t consume_u8(const char* name = "uint8_t") { return consume_little_endian<uint8_t, kTrace>(name); } uint8_t consume_u8(const char* name, ITracer* tracer) { if (tracer) { tracer->Bytes(pc_, sizeof(uint8_t)); tracer->Description(name); } return consume_little_endian<uint8_t, kNoTrace>(name); } // Reads a 16-bit unsigned integer (little endian) and advances {pc_}. uint16_t consume_u16(const char* name = "uint16_t") { return consume_little_endian<uint16_t, kTrace>(name); } // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}. uint32_t consume_u32(const char* name, ITracer* tracer) { if (tracer) { tracer->Bytes(pc_, sizeof(uint32_t)); tracer->Description(name); } return consume_little_endian<uint32_t, kNoTrace>(name); } // Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}. uint32_t consume_u32v(const char* name = "var_uint32") { auto [result, length] = read_leb<uint32_t, FullValidationTag, kTrace>(pc_, name); pc_ += length; return result; } uint32_t consume_u32v(const char* name, ITracer* tracer) { auto [result, length] = read_leb<uint32_t, FullValidationTag, kNoTrace>(pc_, name); if (tracer) { tracer->Bytes(pc_, length); tracer->Description(name); } pc_ += length; return result; } // Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}. int32_t consume_i32v(const char* name = "var_int32") { auto [result, length] = read_leb<int32_t, FullValidationTag, kTrace>(pc_, name); pc_ += length; return result; } // Reads a LEB128 variable-length unsigned 64-bit integer and advances {pc_}. uint64_t consume_u64v(const char* name, ITracer* tracer) { auto [result, length] = read_leb<uint64_t, FullValidationTag, kNoTrace>(pc_, name); if (tracer) { tracer->Bytes(pc_, length); tracer->Description(name); } pc_ += length; return result; } // Reads a LEB128 variable-length signed 64-bit integer and advances {pc_}. int64_t consume_i64v(const char* name = "var_int64") { auto [result, length] = read_leb<int64_t, FullValidationTag, kTrace>(pc_, name); pc_ += length; return result; } // Consume {size} bytes and send them to the bit bucket, advancing {pc_}. void consume_bytes(uint32_t size, const char* name = "skip") { // Only trace if the name is not null. TRACE_IF(name, " +%u %-20s: %u bytes\n", pc_offset(), name, size); if (checkAvailable(size)) { pc_ += size; } else { pc_ = end_; } } void consume_bytes(uint32_t size, const char* name, ITracer* tracer) { if (tracer) { tracer->Bytes(pc_, size); tracer->Description(name); } consume_bytes(size, nullptr); } uint32_t available_bytes() const { DCHECK_LE(pc_, end_); DCHECK_GE(kMaxUInt32, end_ - pc_); return static_cast<uint32_t>(end_ - pc_); } // Check that at least {size} bytes exist between {pc_} and {end_}. bool checkAvailable(uint32_t size) { if (V8_UNLIKELY(size > available_bytes())) { errorf(pc_, "expected %u bytes, fell off end", size); return false; } return true; } // Use this for "boolean validation", i.e. if the error message is not used // anyway. void V8_NOINLINE V8_PRESERVE_MOST MarkError() { if (!ok()) return; error_ = {0, "validation failed"}; onFirstError(); } // Do not inline error methods. This has measurable impact on validation time, // see https://crbug.com/910432. void V8_NOINLINE V8_PRESERVE_MOST error(const char* msg) { errorf(pc_offset(), "%s", msg); } void V8_NOINLINE V8_PRESERVE_MOST error(const uint8_t* pc, const char* msg) { errorf(pc_offset(pc), "%s", msg); } void V8_NOINLINE V8_PRESERVE_MOST error(uint32_t offset, const char* msg) { errorf(offset, "%s", msg); } template <typename... Args> void V8_NOINLINE V8_PRESERVE_MOST errorf(const char* format, Args... args) { errorf(pc_offset(), format, args...); } template <typename... Args> void V8_NOINLINE V8_PRESERVE_MOST errorf(const uint8_t* pc, const char* format, Args... args) { errorf(pc_offset(pc), format, args...); } template <typename... Args> void V8_NOINLINE V8_PRESERVE_MOST errorf(uint32_t offset, const char* format, Args... args) { static_assert( sizeof...(Args) > 0, "Use error instead of errorf if the format string has no placeholders"); verrorf(offset, format, args...); } // Behavior triggered on first error, overridden in subclasses. virtual void onFirstError() {} // Debugging helper to print a bytes range as hex bytes. void traceByteRange(const uint8_t* start, const uint8_t* end) { DCHECK_LE(start, end); for (const uint8_t* p = start; p < end; ++p) TRACE("%02x ", *p); } // Debugging helper to print bytes up to the end. void traceOffEnd() { traceByteRange(pc_, end_); TRACE("<end>\n"); } // Converts the given value to a {Result}, copying the error if necessary. template <typename T, typename R = std::decay_t<T>> Result<R> toResult(T&& val) { if (failed()) { TRACE("Result error: %s\n", error_.message().c_str()); return Result<R>{error_}; } return Result<R>{std::forward<T>(val)}; } // Resets the boundaries of this decoder. void Reset(const uint8_t* start, const uint8_t* end, uint32_t buffer_offset = 0) { DCHECK_LE(start, end); DCHECK_EQ(static_cast<uint32_t>(end - start), end - start); start_ = start; pc_ = start; end_ = end; buffer_offset_ = buffer_offset; error_ = {}; } void Reset(base::Vector<const uint8_t> bytes, uint32_t buffer_offset = 0) { Reset(bytes.begin(), bytes.end(), buffer_offset); } bool ok() const { return !failed(); } bool failed() const { return error_.has_error(); } bool more() const { return pc_ < end_; } const WasmError& error() const { return error_; } const uint8_t* start() const { return start_; } const uint8_t* pc() const { return pc_; } uint32_t V8_INLINE position() const { return static_cast<uint32_t>(pc_ - start_); } // This needs to be inlined for performance (see https://crbug.com/910432). uint32_t V8_INLINE pc_offset(const uint8_t* pc) const { DCHECK_LE(start_, pc); DCHECK_GE(kMaxUInt32 - buffer_offset_, pc - start_); return static_cast<uint32_t>(pc - start_) + buffer_offset_; } uint32_t pc_offset() const { return pc_offset(pc_); } uint32_t buffer_offset() const { return buffer_offset_; } // Takes an offset relative to the module start and returns an offset relative // to the current buffer of the decoder. uint32_t GetBufferRelativeOffset(uint32_t offset) const { DCHECK_LE(buffer_offset_, offset); return offset - buffer_offset_; } const uint8_t* end() const { return end_; } void set_end(const uint8_t* end) { end_ = end; } // Check if the uint8_t at {offset} from the current pc equals {expected}. bool lookahead(int offset, uint8_t expected) { DCHECK_LE(pc_, end_); return end_ - pc_ > offset && pc_[offset] == expected; } protected: const uint8_t* start_; const uint8_t* pc_; const uint8_t* end_; // The offset of the current buffer in the module. Needed for streaming. uint32_t buffer_offset_; WasmError error_; private: void V8_NOINLINE PRINTF_FORMAT(3, 4) verrorf(uint32_t offset, const char* format, ...) { // Only report the first error. if (!ok()) return; constexpr int kMaxErrorMsg = 256; base::EmbeddedVector<char, kMaxErrorMsg> buffer; va_list args; va_start(args, format); int len = base::VSNPrintF(buffer, format, args); va_end(args); CHECK_LT(0, len); error_ = {offset, {buffer.begin(), static_cast<size_t>(len)}}; onFirstError(); } template <typename IntType, typename ValidationTag> IntType read_little_endian(const uint8_t* pc, Name<ValidationTag> msg) { DCHECK_LE(start_, pc); if (!ValidationTag::validate) { DCHECK_LE(pc, end_); DCHECK_LE(sizeof(IntType), end_ - pc); } else if (V8_UNLIKELY(ptrdiff_t{sizeof(IntType)} > end_ - pc)) { if (ValidationTag::full_validation) { error(pc, msg); } else { MarkError(); } return 0; } return base::ReadLittleEndianValue<IntType>(reinterpret_cast<Address>(pc)); } template <typename IntType, TraceFlag trace> IntType consume_little_endian(const char* name) { TRACE_IF(trace, " +%u %-20s: ", pc_offset(), name); if (!checkAvailable(sizeof(IntType))) { traceOffEnd(); pc_ = end_; return IntType{0}; } IntType val = read_little_endian<IntType, NoValidationTag>(pc_, name); traceByteRange(pc_, pc_ + sizeof(IntType)); TRACE_IF(trace, "= %d\n", val); pc_ += sizeof(IntType); return val; } // The implementation of LEB-decoding; returns the value and the number of // bytes read. template <typename IntType, typename ValidationTag, TraceFlag trace, size_t size_in_bits = 8 * sizeof(IntType)> V8_INLINE std::pair<IntType, uint32_t> read_leb( const uint8_t* pc, Name<ValidationTag> name = "varint") { static_assert(size_in_bits <= 8 * sizeof(IntType), "leb does not fit in type"); TRACE_IF(trace, " +%u %-20s: ", pc_offset(), implicit_cast<const char*>(name)); // Fast path for single-byte integers. if (V8_LIKELY((!ValidationTag::validate || pc < end_) && !(*pc & 0x80))) { TRACE_IF(trace, "%02x ", *pc); IntType result = *pc; if (std::is_signed<IntType>::value) { // Perform sign extension. constexpr int sign_ext_shift = int{8 * sizeof(IntType)} - 7; result = (result << sign_ext_shift) >> sign_ext_shift; TRACE_IF(trace, "= %" PRIi64 "\n", static_cast<int64_t>(result)); } else { TRACE_IF(trace, "= %" PRIu64 "\n", static_cast<uint64_t>(result)); } return {result, 1}; } auto [result, length] = read_leb_slowpath<IntType, ValidationTag, trace, size_in_bits>(pc, name); V8_ASSUME(length >= 0 && length <= (size_in_bits + 6) / 7); V8_ASSUME(ValidationTag::validate || length >= 1); return {result, length}; } template <typename IntType, typename ValidationTag, TraceFlag trace, size_t size_in_bits = 8 * sizeof(IntType)> V8_NOINLINE V8_PRESERVE_MOST std::pair<IntType, uint32_t> read_leb_slowpath( const uint8_t* pc, Name<ValidationTag> name) { // Create an unrolled LEB decoding function per integer type. return read_leb_tail<IntType, ValidationTag, trace, size_in_bits, 0>( pc, name, 0); } template <typename IntType, typename ValidationTag, TraceFlag trace, size_t size_in_bits, int byte_index> V8_INLINE std::pair<IntType, uint32_t> read_leb_tail( const uint8_t* pc, Name<ValidationTag> name, IntType intermediate_result) { constexpr bool is_signed = std::is_signed<IntType>::value; constexpr int kMaxLength = (size_in_bits + 6) / 7; static_assert(byte_index < kMaxLength, "invalid template instantiation"); constexpr int shift = byte_index * 7; constexpr bool is_last_byte = byte_index == kMaxLength - 1; const bool at_end = ValidationTag::validate && pc >= end_; uint8_t b = 0; if (V8_LIKELY(!at_end)) { DCHECK_LT(pc, end_); b = *pc; TRACE_IF(trace, "%02x ", b); using Unsigned = typename std::make_unsigned<IntType>::type; intermediate_result |= (static_cast<Unsigned>(static_cast<IntType>(b) & 0x7f) << shift); } if (!is_last_byte && (b & 0x80)) { // Make sure that we only instantiate the template for valid byte indexes. // Compilers are not smart enough to figure out statically that the // following call is unreachable if is_last_byte is false. constexpr int next_byte_index = byte_index + (is_last_byte ? 0 : 1); return read_leb_tail<IntType, ValidationTag, trace, size_in_bits, next_byte_index>(pc + 1, name, intermediate_result); } if (ValidationTag::validate && V8_UNLIKELY(at_end || (b & 0x80))) { TRACE_IF(trace, at_end ? "<end> " : "<length overflow> "); if constexpr (ValidationTag::full_validation) { errorf(pc, "%s while decoding %s", at_end ? "reached end" : "length overflow", name); } else { MarkError(); } return {0, 0}; } if constexpr (is_last_byte) { // A signed-LEB128 must sign-extend the final byte, excluding its // most-significant bit; e.g. for a 32-bit LEB128: // kExtraBits = 4 (== 32 - (5-1) * 7) // For unsigned values, the extra bits must be all zero. // For signed values, the extra bits *plus* the most significant bit must // either be 0, or all ones. constexpr int kExtraBits = size_in_bits - ((kMaxLength - 1) * 7); constexpr int kSignExtBits = kExtraBits - (is_signed ? 1 : 0); const uint8_t checked_bits = b & (0xFF << kSignExtBits); constexpr uint8_t kSignExtendedExtraBits = 0x7f & (0xFF << kSignExtBits); const bool valid_extra_bits = checked_bits == 0 || (is_signed && checked_bits == kSignExtendedExtraBits); if (!ValidationTag::validate) { DCHECK(valid_extra_bits); } else if (V8_UNLIKELY(!valid_extra_bits)) { if (ValidationTag::full_validation) { error(pc, "extra bits in varint"); } else { MarkError(); } return {0, 0}; } } constexpr int sign_ext_shift = is_signed ? std::max(0, int{8 * sizeof(IntType)} - shift - 7) : 0; // Perform sign extension. intermediate_result = (intermediate_result << sign_ext_shift) >> sign_ext_shift; if (trace && is_signed) { TRACE("= %" PRIi64 "\n", static_cast<int64_t>(intermediate_result)); } else if (trace) { TRACE("= %" PRIu64 "\n", static_cast<uint64_t>(intermediate_result)); } const uint32_t length = byte_index + 1; return {intermediate_result, length}; } }; #undef TRACE } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_DECODER_H_