Line data Source code
1 : // Copyright 2015 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_WASM_DECODER_H_
6 : #define V8_WASM_DECODER_H_
7 :
8 : #include <cstdarg>
9 : #include <memory>
10 :
11 : #include "src/base/compiler-specific.h"
12 : #include "src/flags.h"
13 : #include "src/signature.h"
14 : #include "src/utils.h"
15 : #include "src/wasm/wasm-result.h"
16 : #include "src/zone/zone-containers.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 : namespace wasm {
21 :
22 : #if DEBUG
23 : #define TRACE(...) \
24 : do { \
25 : if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \
26 : } while (false)
27 : #define TRACE_IF(cond, ...) \
28 : do { \
29 : if (FLAG_trace_wasm_decoder && (cond)) PrintF(__VA_ARGS__); \
30 : } while (false)
31 : #else
32 : #define TRACE(...)
33 : #define TRACE_IF(...)
34 : #endif
35 :
36 : // A helper utility to decode bytes, integers, fields, varints, etc, from
37 : // a buffer of bytes.
38 : class Decoder {
39 : public:
40 : Decoder(const byte* start, const byte* end)
41 3469898 : : start_(start), pc_(start), end_(end), error_pc_(nullptr) {}
42 : Decoder(const byte* start, const byte* pc, const byte* end)
43 15400 : : start_(start), pc_(pc), end_(end), error_pc_(nullptr) {}
44 :
45 3485229 : virtual ~Decoder() {}
46 :
47 : inline bool check(const byte* pc, unsigned length, const char* msg) {
48 : DCHECK_LE(start_, pc);
49 895032 : if (V8_UNLIKELY(pc + length > end_)) {
50 : error(pc, msg);
51 : return false;
52 : }
53 : return true;
54 : }
55 :
56 : // Reads an 8-bit unsigned integer.
57 : template <bool checked>
58 : inline uint8_t read_u8(const byte* pc, const char* msg = "expected 1 byte") {
59 441633 : return read_little_endian<uint8_t, checked>(pc, msg);
60 : }
61 :
62 : // Reads a 16-bit unsigned integer (little endian).
63 : template <bool checked>
64 : inline uint16_t read_u16(const byte* pc,
65 : const char* msg = "expected 2 bytes") {
66 : return read_little_endian<uint16_t, checked>(pc, msg);
67 : }
68 :
69 : // Reads a 32-bit unsigned integer (little endian).
70 : template <bool checked>
71 : inline uint32_t read_u32(const byte* pc,
72 : const char* msg = "expected 4 bytes") {
73 442092 : return read_little_endian<uint32_t, checked>(pc, msg);
74 : }
75 :
76 : // Reads a 64-bit unsigned integer (little endian).
77 : template <bool checked>
78 : inline uint64_t read_u64(const byte* pc,
79 : const char* msg = "expected 8 bytes") {
80 11307 : return read_little_endian<uint64_t, checked>(pc, msg);
81 : }
82 :
83 : // Reads a variable-length unsigned integer (little endian).
84 : template <bool checked>
85 : uint32_t read_u32v(const byte* pc, unsigned* length,
86 : const char* name = "LEB32") {
87 1477737 : return read_leb<uint32_t, checked, false, false>(pc, length, name);
88 : }
89 :
90 : // Reads a variable-length signed integer (little endian).
91 : template <bool checked>
92 : int32_t read_i32v(const byte* pc, unsigned* length,
93 : const char* name = "signed LEB32") {
94 862751 : return read_leb<int32_t, checked, false, false>(pc, length, name);
95 : }
96 :
97 : // Reads a variable-length unsigned integer (little endian).
98 : template <bool checked>
99 : uint64_t read_u64v(const byte* pc, unsigned* length,
100 : const char* name = "LEB64") {
101 : return read_leb<uint64_t, checked, false, false>(pc, length, name);
102 : }
103 :
104 : // Reads a variable-length signed integer (little endian).
105 : template <bool checked>
106 : int64_t read_i64v(const byte* pc, unsigned* length,
107 : const char* name = "signed LEB64") {
108 10376 : return read_leb<int64_t, checked, false, false>(pc, length, name);
109 : }
110 :
111 : // Reads a 8-bit unsigned integer (byte) and advances {pc_}.
112 : uint8_t consume_u8(const char* name = "uint8_t") {
113 2126521 : return consume_little_endian<uint8_t>(name);
114 : }
115 :
116 : // Reads a 16-bit unsigned integer (little endian) and advances {pc_}.
117 : uint16_t consume_u16(const char* name = "uint16_t") {
118 : return consume_little_endian<uint16_t>(name);
119 : }
120 :
121 : // Reads a single 32-bit unsigned integer (little endian) and advances {pc_}.
122 : uint32_t consume_u32(const char* name = "uint32_t") {
123 39172 : return consume_little_endian<uint32_t>(name);
124 : }
125 :
126 : // Reads a LEB128 variable-length unsigned 32-bit integer and advances {pc_}.
127 : uint32_t consume_u32v(const char* name = nullptr) {
128 942895 : unsigned length = 0;
129 942895 : return read_leb<uint32_t, true, true, true>(pc_, &length, name);
130 : }
131 :
132 : // Reads a LEB128 variable-length signed 32-bit integer and advances {pc_}.
133 : int32_t consume_i32v(const char* name = nullptr) {
134 278 : unsigned length = 0;
135 278 : return read_leb<int32_t, true, true, true>(pc_, &length, name);
136 : }
137 :
138 : // Consume {size} bytes and send them to the bit bucket, advancing {pc_}.
139 650867 : void consume_bytes(uint32_t size, const char* name = "skip") {
140 : // Only trace if the name is not null.
141 : TRACE_IF(name, " +%d %-20s: %d bytes\n", static_cast<int>(pc_ - start_),
142 : name, size);
143 650867 : if (checkAvailable(size)) {
144 650849 : pc_ += size;
145 : } else {
146 18 : pc_ = end_;
147 : }
148 650867 : }
149 :
150 : // Check that at least {size} bytes exist between {pc_} and {end_}.
151 2938636 : bool checkAvailable(int size) {
152 2938636 : intptr_t pc_overflow_value = std::numeric_limits<intptr_t>::max() - size;
153 2938636 : if (size < 0 || (intptr_t)pc_ > pc_overflow_value) {
154 0 : errorf(pc_, "reading %d bytes would underflow/overflow", size);
155 0 : return false;
156 2938636 : } else if (pc_ < start_ || end_ < (pc_ + size)) {
157 613 : errorf(pc_, "expected %d bytes, fell off end", size);
158 611 : return false;
159 : } else {
160 : return true;
161 : }
162 : }
163 :
164 347 : void error(const char* msg) { errorf(pc_, "%s", msg); }
165 :
166 1287 : void error(const byte* pc, const char* msg) { errorf(pc, "%s", msg); }
167 :
168 : // Sets internal error state.
169 3451 : void PRINTF_FORMAT(3, 4) errorf(const byte* pc, const char* format, ...) {
170 : // Only report the first error.
171 4964 : if (!ok()) return;
172 : #if DEBUG
173 : if (FLAG_wasm_break_on_decoder_error) {
174 : base::OS::DebugBreak();
175 : }
176 : #endif
177 : constexpr int kMaxErrorMsg = 256;
178 : EmbeddedVector<char, kMaxErrorMsg> buffer;
179 : va_list arguments;
180 1938 : va_start(arguments, format);
181 1938 : int len = VSNPrintF(buffer, format, arguments);
182 1938 : CHECK_LT(0, len);
183 1938 : va_end(arguments);
184 1938 : error_msg_.assign(buffer.start(), len);
185 1938 : error_pc_ = pc;
186 1938 : onFirstError();
187 : }
188 :
189 : // Behavior triggered on first error, overridden in subclasses.
190 188 : virtual void onFirstError() {}
191 :
192 : // Debugging helper to print a bytes range as hex bytes.
193 : void traceByteRange(const byte* start, const byte* end) {
194 : DCHECK_LE(start, end);
195 : for (const byte* p = start; p < end; ++p) TRACE("%02x ", *p);
196 : }
197 :
198 : // Debugging helper to print bytes up to the end.
199 : void traceOffEnd() {
200 : traceByteRange(pc_, end_);
201 : TRACE("<end>\n");
202 : }
203 :
204 : // Converts the given value to a {Result}, copying the error if necessary.
205 : template <typename T, typename U = typename std::remove_reference<T>::type>
206 78 : Result<U> toResult(T&& val) {
207 : Result<U> result(std::forward<T>(val));
208 106201 : if (failed()) {
209 : // The error message must not be empty, otherwise Result::failed() will be
210 : // false.
211 : DCHECK(!error_msg_.empty());
212 : TRACE("Result error: %s\n", error_msg_.c_str());
213 : DCHECK_GE(error_pc_, start_);
214 11670 : result.error_offset = static_cast<uint32_t>(error_pc_ - start_);
215 888 : result.error_msg = std::move(error_msg_);
216 : }
217 78 : return result;
218 : }
219 :
220 : // Resets the boundaries of this decoder.
221 : void Reset(const byte* start, const byte* end) {
222 1413044 : start_ = start;
223 1413044 : pc_ = start;
224 1413044 : end_ = end;
225 1413044 : error_pc_ = nullptr;
226 1413044 : error_msg_.clear();
227 : }
228 :
229 : bool ok() const { return error_msg_.empty(); }
230 : bool failed() const { return !ok(); }
231 : bool more() const { return pc_ < end_; }
232 :
233 : const byte* start() const { return start_; }
234 : const byte* pc() const { return pc_; }
235 212939 : uint32_t pc_offset() const { return static_cast<uint32_t>(pc_ - start_); }
236 : const byte* end() const { return end_; }
237 :
238 : protected:
239 : const byte* start_;
240 : const byte* pc_;
241 : const byte* end_;
242 : const byte* error_pc_;
243 : std::string error_msg_;
244 :
245 : private:
246 : template <typename IntType, bool checked>
247 895032 : inline IntType read_little_endian(const byte* pc, const char* msg) {
248 : if (!checked) {
249 : DCHECK(check(pc, sizeof(IntType), msg));
250 895032 : } else if (!check(pc, sizeof(IntType), msg)) {
251 : return IntType{0};
252 : }
253 895032 : return ReadLittleEndianValue<IntType>(pc);
254 : }
255 :
256 : template <typename IntType>
257 2165693 : inline IntType consume_little_endian(const char* name) {
258 : TRACE(" +%d %-20s: ", static_cast<int>(pc_ - start_), name);
259 2165693 : if (!checkAvailable(sizeof(IntType))) {
260 : traceOffEnd();
261 410 : pc_ = end_;
262 : return IntType{0};
263 : }
264 2165283 : IntType val = read_little_endian<IntType, false>(pc_, name);
265 : traceByteRange(pc_, pc_ + sizeof(IntType));
266 : TRACE("= %d\n", val);
267 2165283 : pc_ += sizeof(IntType);
268 : return val;
269 : }
270 :
271 : template <typename IntType, bool checked, bool advance_pc, bool trace>
272 3294044 : inline IntType read_leb(const byte* pc, unsigned* length,
273 : const char* name = "varint") {
274 : DCHECK_IMPLIES(advance_pc, pc == pc_);
275 : constexpr bool is_signed = std::is_signed<IntType>::value;
276 : TRACE_IF(trace, " +%d %-20s: ", static_cast<int>(pc - start_), name);
277 : constexpr int kMaxLength = (sizeof(IntType) * 8 + 6) / 7;
278 : const byte* ptr = pc;
279 3294044 : const byte* end = Min(end_, ptr + kMaxLength);
280 : // The end variable is only used if checked == true. MSVC recognizes this.
281 : USE(end);
282 : int shift = 0;
283 : byte b = 0;
284 : IntType result = 0;
285 13438671 : do {
286 4302087 : if (checked && V8_UNLIKELY(ptr >= end)) {
287 : TRACE_IF(trace,
288 : ptr == pc + kMaxLength ? "<length overflow> " : "<end> ");
289 565 : errorf(ptr, "expected %s", name);
290 : result = 0;
291 565 : break;
292 : }
293 : DCHECK_GT(end, ptr);
294 13438671 : b = *ptr++;
295 : TRACE_IF(trace, "%02x ", b);
296 13277606 : result = result | ((static_cast<IntType>(b) & 0x7F) << shift);
297 13277606 : shift += 7;
298 : } while (b & 0x80);
299 : DCHECK_LE(ptr - pc, kMaxLength);
300 11515693 : *length = static_cast<unsigned>(ptr - pc);
301 943169 : if (advance_pc) pc_ = ptr;
302 3294044 : if (*length == kMaxLength) {
303 : // A signed-LEB128 must sign-extend the final byte, excluding its
304 : // most-significant bit; e.g. for a 32-bit LEB128:
305 : // kExtraBits = 4 (== 32 - (5-1) * 7)
306 : // For unsigned values, the extra bits must be all zero.
307 : // For signed values, the extra bits *plus* the most significant bit must
308 : // either be 0, or all ones.
309 : constexpr int kExtraBits = (sizeof(IntType) * 8) - ((kMaxLength - 1) * 7);
310 : constexpr int kSignExtBits = kExtraBits - (is_signed ? 1 : 0);
311 154975 : const byte checked_bits = b & (0xFF << kSignExtBits);
312 : constexpr byte kSignExtendedExtraBits = 0x7f & (0xFF << kSignExtBits);
313 : bool valid_extra_bits =
314 : checked_bits == 0 ||
315 42449 : (is_signed && checked_bits == kSignExtendedExtraBits);
316 : if (!checked) {
317 : DCHECK(valid_extra_bits);
318 163152 : } else if (!valid_extra_bits) {
319 : error(ptr, "extra bits in varint");
320 : result = 0;
321 : }
322 : }
323 1456615 : if (is_signed && *length < kMaxLength) {
324 1409196 : int sign_ext_shift = 8 * sizeof(IntType) - shift;
325 : // Perform sign extension.
326 1409196 : result = (result << sign_ext_shift) >> sign_ext_shift;
327 : }
328 : if (trace && is_signed) {
329 : TRACE("= %" PRIi64 "\n", static_cast<int64_t>(result));
330 : } else if (trace) {
331 : TRACE("= %" PRIu64 "\n", static_cast<uint64_t>(result));
332 : }
333 3294029 : return result;
334 : }
335 : };
336 :
337 : #undef TRACE
338 : } // namespace wasm
339 : } // namespace internal
340 : } // namespace v8
341 :
342 : #endif // V8_WASM_DECODER_H_
|