wasmparser/
binary_reader.rs

1/* Copyright 2018 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16use crate::prelude::*;
17use crate::{limits::*, *};
18use core::fmt;
19use core::marker;
20use core::ops::Range;
21use core::str;
22
23pub(crate) const WASM_MAGIC_NUMBER: &[u8; 4] = b"\0asm";
24
25/// A binary reader for WebAssembly modules.
26#[derive(Debug, Clone)]
27pub struct BinaryReaderError {
28    // Wrap the actual error data in a `Box` so that the error is just one
29    // word. This means that we can continue returning small `Result`s in
30    // registers.
31    pub(crate) inner: Box<BinaryReaderErrorInner>,
32}
33
34#[derive(Debug, Clone)]
35pub(crate) struct BinaryReaderErrorInner {
36    pub(crate) message: String,
37    pub(crate) kind: BinaryReaderErrorKind,
38    pub(crate) offset: usize,
39    pub(crate) needed_hint: Option<usize>,
40}
41
42#[derive(Debug, Clone, Copy)]
43pub(crate) enum BinaryReaderErrorKind {
44    Custom,
45    Invalid,
46}
47
48/// The result for `BinaryReader` operations.
49pub type Result<T, E = BinaryReaderError> = core::result::Result<T, E>;
50
51#[cfg(feature = "std")]
52impl std::error::Error for BinaryReaderError {}
53
54#[cfg(all(not(feature = "std"), core_error))]
55impl core::error::Error for BinaryReaderError {}
56
57impl fmt::Display for BinaryReaderError {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        write!(
60            f,
61            "{} (at offset 0x{:x})",
62            self.inner.message, self.inner.offset
63        )
64    }
65}
66
67impl BinaryReaderError {
68    #[cold]
69    pub(crate) fn _new(kind: BinaryReaderErrorKind, message: String, offset: usize) -> Self {
70        BinaryReaderError {
71            inner: Box::new(BinaryReaderErrorInner {
72                kind,
73                message,
74                offset,
75                needed_hint: None,
76            }),
77        }
78    }
79
80    #[cold]
81    pub(crate) fn new(message: impl Into<String>, offset: usize) -> Self {
82        Self::_new(BinaryReaderErrorKind::Custom, message.into(), offset)
83    }
84
85    #[cold]
86    pub(crate) fn invalid(msg: &'static str, offset: usize) -> Self {
87        Self::_new(BinaryReaderErrorKind::Invalid, msg.into(), offset)
88    }
89
90    #[cold]
91    pub(crate) fn fmt(args: fmt::Arguments<'_>, offset: usize) -> Self {
92        BinaryReaderError::new(args.to_string(), offset)
93    }
94
95    #[cold]
96    pub(crate) fn eof(offset: usize, needed_hint: usize) -> Self {
97        let mut err = BinaryReaderError::new("unexpected end-of-file", offset);
98        err.inner.needed_hint = Some(needed_hint);
99        err
100    }
101
102    pub(crate) fn kind(&mut self) -> BinaryReaderErrorKind {
103        self.inner.kind
104    }
105
106    /// Get this error's message.
107    pub fn message(&self) -> &str {
108        &self.inner.message
109    }
110
111    /// Get the offset within the Wasm binary where the error occurred.
112    pub fn offset(&self) -> usize {
113        self.inner.offset
114    }
115
116    #[cfg(all(feature = "validate", feature = "component-model"))]
117    pub(crate) fn add_context(&mut self, context: String) {
118        self.inner.message = format!("{context}\n{}", self.inner.message);
119    }
120
121    pub(crate) fn set_message(&mut self, message: &str) {
122        self.inner.message = message.to_string();
123    }
124}
125
126/// A binary reader of the WebAssembly structures and types.
127#[derive(Clone, Debug, Hash)]
128pub struct BinaryReader<'a> {
129    buffer: &'a [u8],
130    position: usize,
131    original_offset: usize,
132
133    // When the `features` feature is disabled then the `WasmFeatures` type
134    // still exists but this field is still omitted. When `features` is
135    // disabled then the only constructor of this type is `BinaryReader::new`
136    // which documents all known features being active. All known features
137    // being active isn't represented by `WasmFeatures` when the feature is
138    // disabled so the field is omitted here to prevent accidentally using the
139    // wrong listing of features.
140    //
141    // Feature accessors are defined by `foreach_wasm_feature!` below with a
142    // method-per-feature on `BinaryReader` which when the `features` feature
143    // is disabled returns `true` by default.
144    #[cfg(feature = "features")]
145    features: WasmFeatures,
146}
147
148impl<'a> BinaryReader<'a> {
149    /// Creates a new binary reader which will parse the `data` provided.
150    ///
151    /// The `original_offset` provided is used for byte offsets in errors that
152    /// are generated. That offset is added to the current position in `data`.
153    /// This can be helpful when `data` is just a window of a view into a larger
154    /// wasm binary perhaps not even entirely stored locally.
155    ///
156    /// The returned binary reader will have all features known to this crate
157    /// enabled. To reject binaries that aren't valid unless a certain feature
158    /// is enabled use the [`BinaryReader::new_features`] constructor instead.
159    pub fn new(data: &[u8], original_offset: usize) -> BinaryReader<'_> {
160        BinaryReader {
161            buffer: data,
162            position: 0,
163            original_offset,
164            #[cfg(feature = "features")]
165            features: WasmFeatures::all(),
166        }
167    }
168
169    /// Creates a new binary reader which will parse the `data` provided.
170    ///
171    /// The `original_offset` provided is used for byte offsets in errors that
172    /// are generated. That offset is added to the current position in `data`.
173    /// This can be helpful when `data` is just a window of a view into a larger
174    /// wasm binary perhaps not even entirely stored locally.
175    ///
176    /// The `features` argument provided controls which WebAssembly features are
177    /// active when parsing this data. Wasm features typically don't affect
178    /// parsing too much and are generally more applicable during
179    /// validation, but features and proposals will often reinterpret
180    /// previously-invalid constructs as now-valid things meaning something
181    /// slightly different. This means that invalid bytes before a feature may
182    /// now be interpreted differently after a feature is implemented. This
183    /// means that the set of activated features can affect what errors are
184    /// generated and when they are generated.
185    ///
186    /// In general it's safe to pass `WasmFeatures::all()` here. There's no
187    /// downside to enabling all features while parsing and only enabling a
188    /// subset of features during validation.
189    ///
190    /// Note that the activated set of features does not guarantee that
191    /// `BinaryReader` will return an error for disabled features. For example
192    /// if SIMD is disabled then SIMD instructions will still be parsed via
193    /// [`OperatorsReader::visit_operator`]. Validation must still be performed to
194    /// provide a strict guarantee that if a feature is disabled that a binary
195    /// doesn't leverage the feature. The activated set of features here instead
196    /// only affects locations where preexisting bytes are reinterpreted in
197    /// different ways with future proposals, such as the `memarg` moving from a
198    /// 32-bit offset to a 64-bit offset with the `memory64` proposal.
199    #[cfg(feature = "features")]
200    pub fn new_features(
201        data: &[u8],
202        original_offset: usize,
203        features: WasmFeatures,
204    ) -> BinaryReader<'_> {
205        BinaryReader {
206            buffer: data,
207            position: 0,
208            original_offset,
209            features,
210        }
211    }
212
213    /// "Shrinks" this binary reader to retain only the buffer left-to-parse.
214    ///
215    /// The primary purpose of this method is to change the return value of the
216    /// `range()` method. That method returns the range of the original buffer
217    /// within the wasm binary so calling `range()` on the returned
218    /// `BinaryReader` will return a smaller range than if `range()` is called
219    /// on `self`.
220    ///
221    /// Otherwise parsing values from either `self` or the return value should
222    /// return the same thing.
223    pub(crate) fn shrink(&self) -> BinaryReader<'a> {
224        BinaryReader {
225            buffer: &self.buffer[self.position..],
226            position: 0,
227            original_offset: self.original_offset + self.position,
228            #[cfg(feature = "features")]
229            features: self.features,
230        }
231    }
232
233    /// Gets the original position of the binary reader.
234    #[inline]
235    pub fn original_position(&self) -> usize {
236        self.original_offset + self.position
237    }
238
239    /// Returns the currently active set of wasm features that this reader is
240    /// using while parsing.
241    ///
242    /// For more information see [`BinaryReader::new`].
243    #[cfg(feature = "features")]
244    pub fn features(&self) -> WasmFeatures {
245        self.features
246    }
247
248    /// Sets the wasm features active while parsing to the `features` specified.
249    ///
250    /// For more information see [`BinaryReader::new`].
251    #[cfg(feature = "features")]
252    pub fn set_features(&mut self, features: WasmFeatures) {
253        self.features = features;
254    }
255
256    /// Returns a range from the starting offset to the end of the buffer.
257    pub fn range(&self) -> Range<usize> {
258        self.original_offset..self.original_offset + self.buffer.len()
259    }
260
261    pub(crate) fn remaining_buffer(&self) -> &'a [u8] {
262        &self.buffer[self.position..]
263    }
264
265    fn ensure_has_byte(&self) -> Result<()> {
266        if self.position < self.buffer.len() {
267            Ok(())
268        } else {
269            Err(BinaryReaderError::eof(self.original_position(), 1))
270        }
271    }
272
273    pub(crate) fn ensure_has_bytes(&self, len: usize) -> Result<()> {
274        if self.position + len <= self.buffer.len() {
275            Ok(())
276        } else {
277            let hint = self.position + len - self.buffer.len();
278            Err(BinaryReaderError::eof(self.original_position(), hint))
279        }
280    }
281
282    /// Reads a value of type `T` from this binary reader, advancing the
283    /// internal position in this reader forward as data is read.
284    #[inline]
285    pub fn read<T>(&mut self) -> Result<T>
286    where
287        T: FromReader<'a>,
288    {
289        T::from_reader(self)
290    }
291
292    pub(crate) fn read_u7(&mut self) -> Result<u8> {
293        let b = self.read_u8()?;
294        if (b & 0x80) != 0 {
295            return Err(BinaryReaderError::new(
296                "invalid u7",
297                self.original_position() - 1,
298            ));
299        }
300        Ok(b)
301    }
302
303    pub(crate) fn external_kind_from_byte(byte: u8, offset: usize) -> Result<ExternalKind> {
304        match byte {
305            0x00 => Ok(ExternalKind::Func),
306            0x01 => Ok(ExternalKind::Table),
307            0x02 => Ok(ExternalKind::Memory),
308            0x03 => Ok(ExternalKind::Global),
309            0x04 => Ok(ExternalKind::Tag),
310            x => Err(Self::invalid_leading_byte_error(x, "external kind", offset)),
311        }
312    }
313
314    /// Reads a variable-length 32-bit size from the byte stream while checking
315    /// against a limit.
316    pub fn read_size(&mut self, limit: usize, desc: &str) -> Result<usize> {
317        let pos = self.original_position();
318        let size = self.read_var_u32()? as usize;
319        if size > limit {
320            bail!(pos, "{desc} size is out of bounds");
321        }
322        Ok(size)
323    }
324
325    /// Reads a variable-length 32-bit size from the byte stream while checking
326    /// against a limit.
327    ///
328    /// Then reads that many values of type `T` and returns them as an iterator.
329    ///
330    /// Note that regardless of how many items are read from the returned
331    /// iterator the items will still be parsed from this reader.
332    pub fn read_iter<'me, T>(
333        &'me mut self,
334        limit: usize,
335        desc: &str,
336    ) -> Result<BinaryReaderIter<'a, 'me, T>>
337    where
338        T: FromReader<'a>,
339    {
340        let size = self.read_size(limit, desc)?;
341        Ok(BinaryReaderIter {
342            remaining: size,
343            reader: self,
344            _marker: marker::PhantomData,
345        })
346    }
347
348    /// Returns whether the `BinaryReader` has reached the end of the file.
349    #[inline]
350    pub fn eof(&self) -> bool {
351        self.position >= self.buffer.len()
352    }
353
354    /// Returns the `BinaryReader`'s current position.
355    #[inline]
356    pub fn current_position(&self) -> usize {
357        self.position
358    }
359
360    /// Returns the number of bytes remaining in the `BinaryReader`.
361    #[inline]
362    pub fn bytes_remaining(&self) -> usize {
363        self.buffer.len() - self.position
364    }
365
366    /// Advances the `BinaryReader` `size` bytes, and returns a slice from the
367    /// current position of `size` length.
368    ///
369    /// # Errors
370    /// If `size` exceeds the remaining length in `BinaryReader`.
371    pub fn read_bytes(&mut self, size: usize) -> Result<&'a [u8]> {
372        self.ensure_has_bytes(size)?;
373        let start = self.position;
374        self.position += size;
375        Ok(&self.buffer[start..self.position])
376    }
377
378    /// Reads a length-prefixed list of bytes from this reader and returns a
379    /// new `BinaryReader` to read that list of bytes.
380    pub fn read_reader(&mut self) -> Result<BinaryReader<'a>> {
381        let size = self.read_var_u32()? as usize;
382        self.skip(|reader| {
383            reader.read_bytes(size)?;
384            Ok(())
385        })
386    }
387
388    /// Advances the `BinaryReader` four bytes and returns a `u32`.
389    /// # Errors
390    /// If `BinaryReader` has less than four bytes remaining.
391    pub fn read_u32(&mut self) -> Result<u32> {
392        self.ensure_has_bytes(4)?;
393        let word = u32::from_le_bytes(
394            self.buffer[self.position..self.position + 4]
395                .try_into()
396                .unwrap(),
397        );
398        self.position += 4;
399        Ok(word)
400    }
401
402    /// Advances the `BinaryReader` eight bytes and returns a `u64`.
403    /// # Errors
404    /// If `BinaryReader` has less than eight bytes remaining.
405    pub fn read_u64(&mut self) -> Result<u64> {
406        self.ensure_has_bytes(8)?;
407        let word = u64::from_le_bytes(
408            self.buffer[self.position..self.position + 8]
409                .try_into()
410                .unwrap(),
411        );
412        self.position += 8;
413        Ok(word)
414    }
415
416    /// Advances the `BinaryReader` a single byte.
417    ///
418    /// # Errors
419    ///
420    /// If `BinaryReader` has no bytes remaining.
421    #[inline]
422    pub fn read_u8(&mut self) -> Result<u8> {
423        let b = match self.buffer.get(self.position) {
424            Some(b) => *b,
425            None => return Err(self.eof_err()),
426        };
427        self.position += 1;
428        Ok(b)
429    }
430
431    #[cold]
432    fn eof_err(&self) -> BinaryReaderError {
433        BinaryReaderError::eof(self.original_position(), 1)
434    }
435
436    /// Advances the `BinaryReader` up to four bytes to parse a variable
437    /// length integer as a `u32`.
438    ///
439    /// # Errors
440    ///
441    /// If `BinaryReader` has less than one or up to four bytes remaining, or
442    /// the integer is larger than 32 bits.
443    #[inline]
444    pub fn read_var_u32(&mut self) -> Result<u32> {
445        // Optimization for single byte i32.
446        let byte = self.read_u8()?;
447        if (byte & 0x80) == 0 {
448            Ok(u32::from(byte))
449        } else {
450            self.read_var_u32_big(byte)
451        }
452    }
453
454    fn read_var_u32_big(&mut self, byte: u8) -> Result<u32> {
455        let mut result = (byte & 0x7F) as u32;
456        let mut shift = 7;
457        loop {
458            let byte = self.read_u8()?;
459            result |= ((byte & 0x7F) as u32) << shift;
460            if shift >= 25 && (byte >> (32 - shift)) != 0 {
461                let msg = if byte & 0x80 != 0 {
462                    "invalid var_u32: integer representation too long"
463                } else {
464                    "invalid var_u32: integer too large"
465                };
466                // The continuation bit or unused bits are set.
467                return Err(BinaryReaderError::new(msg, self.original_position() - 1));
468            }
469            shift += 7;
470            if (byte & 0x80) == 0 {
471                break;
472            }
473        }
474        Ok(result)
475    }
476
477    /// Advances the `BinaryReader` up to four bytes to parse a variable
478    /// length integer as a `u64`.
479    ///
480    /// # Errors
481    ///
482    /// If `BinaryReader` has less than one or up to eight bytes remaining, or
483    /// the integer is larger than 64 bits.
484    #[inline]
485    pub fn read_var_u64(&mut self) -> Result<u64> {
486        // Optimization for single byte u64.
487        let byte = u64::from(self.read_u8()?);
488        if (byte & 0x80) == 0 {
489            Ok(byte)
490        } else {
491            self.read_var_u64_big(byte)
492        }
493    }
494
495    fn read_var_u64_big(&mut self, byte: u64) -> Result<u64> {
496        let mut result = byte & 0x7F;
497        let mut shift = 7;
498        loop {
499            let byte = u64::from(self.read_u8()?);
500            result |= (byte & 0x7F) << shift;
501            if shift >= 57 && (byte >> (64 - shift)) != 0 {
502                let msg = if byte & 0x80 != 0 {
503                    "invalid var_u64: integer representation too long"
504                } else {
505                    "invalid var_u64: integer too large"
506                };
507                // The continuation bit or unused bits are set.
508                return Err(BinaryReaderError::new(msg, self.original_position() - 1));
509            }
510            shift += 7;
511            if (byte & 0x80) == 0 {
512                break;
513            }
514        }
515        Ok(result)
516    }
517
518    /// Executes `f` to skip some data in this binary reader and then returns a
519    /// reader which will read the skipped data.
520    pub fn skip(&mut self, f: impl FnOnce(&mut Self) -> Result<()>) -> Result<Self> {
521        let start = self.position;
522        f(self)?;
523        let mut ret = self.clone();
524        ret.buffer = &self.buffer[start..self.position];
525        ret.position = 0;
526        ret.original_offset = self.original_offset + start;
527        Ok(ret)
528    }
529
530    /// Advances the `BinaryReader` past a WebAssembly string. This method does
531    /// not perform any utf-8 validation.
532    /// # Errors
533    /// If `BinaryReader` has less than four bytes, the string's length exceeds
534    /// the remaining bytes, or the string length
535    /// exceeds `limits::MAX_WASM_STRING_SIZE`.
536    pub fn skip_string(&mut self) -> Result<()> {
537        let len = self.read_var_u32()? as usize;
538        if len > MAX_WASM_STRING_SIZE {
539            return Err(BinaryReaderError::new(
540                "string size out of bounds",
541                self.original_position() - 1,
542            ));
543        }
544        self.ensure_has_bytes(len)?;
545        self.position += len;
546        Ok(())
547    }
548
549    /// Advances the `BinaryReader` up to four bytes to parse a variable
550    /// length integer as a `i32`.
551    /// # Errors
552    /// If `BinaryReader` has less than one or up to four bytes remaining, or
553    /// the integer is larger than 32 bits.
554    #[inline]
555    pub fn read_var_i32(&mut self) -> Result<i32> {
556        // Optimization for single byte i32.
557        let byte = self.read_u8()?;
558        if (byte & 0x80) == 0 {
559            Ok(((byte as i32) << 25) >> 25)
560        } else {
561            self.read_var_i32_big(byte)
562        }
563    }
564
565    fn read_var_i32_big(&mut self, byte: u8) -> Result<i32> {
566        let mut result = (byte & 0x7F) as i32;
567        let mut shift = 7;
568        loop {
569            let byte = self.read_u8()?;
570            result |= ((byte & 0x7F) as i32) << shift;
571            if shift >= 25 {
572                let continuation_bit = (byte & 0x80) != 0;
573                let sign_and_unused_bit = (byte << 1) as i8 >> (32 - shift);
574                if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) {
575                    let msg = if continuation_bit {
576                        "invalid var_i32: integer representation too long"
577                    } else {
578                        "invalid var_i32: integer too large"
579                    };
580                    return Err(BinaryReaderError::new(msg, self.original_position() - 1));
581                }
582                return Ok(result);
583            }
584            shift += 7;
585            if (byte & 0x80) == 0 {
586                break;
587            }
588        }
589        let ashift = 32 - shift;
590        Ok((result << ashift) >> ashift)
591    }
592
593    /// Advances the `BinaryReader` up to four bytes to parse a variable
594    /// length integer as a signed 33 bit integer, returned as a `i64`.
595    /// # Errors
596    /// If `BinaryReader` has less than one or up to five bytes remaining, or
597    /// the integer is larger than 33 bits.
598    pub fn read_var_s33(&mut self) -> Result<i64> {
599        // Optimization for single byte.
600        let byte = self.read_u8()?;
601        if (byte & 0x80) == 0 {
602            return Ok(((byte as i8) << 1) as i64 >> 1);
603        }
604
605        let mut result = (byte & 0x7F) as i64;
606        let mut shift = 7;
607        loop {
608            let byte = self.read_u8()?;
609            result |= ((byte & 0x7F) as i64) << shift;
610            if shift >= 25 {
611                let continuation_bit = (byte & 0x80) != 0;
612                let sign_and_unused_bit = (byte << 1) as i8 >> (33 - shift);
613                if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) {
614                    return Err(BinaryReaderError::new(
615                        "invalid var_s33: integer representation too long",
616                        self.original_position() - 1,
617                    ));
618                }
619                return Ok(result);
620            }
621            shift += 7;
622            if (byte & 0x80) == 0 {
623                break;
624            }
625        }
626        let ashift = 64 - shift;
627        Ok((result << ashift) >> ashift)
628    }
629
630    /// Advances the `BinaryReader` up to eight bytes to parse a variable
631    /// length integer as a 64 bit integer, returned as a `i64`.
632    /// # Errors
633    /// If `BinaryReader` has less than one or up to eight bytes remaining, or
634    /// the integer is larger than 64 bits.
635    pub fn read_var_i64(&mut self) -> Result<i64> {
636        let mut result: i64 = 0;
637        let mut shift = 0;
638        loop {
639            let byte = self.read_u8()?;
640            result |= i64::from(byte & 0x7F) << shift;
641            if shift >= 57 {
642                let continuation_bit = (byte & 0x80) != 0;
643                let sign_and_unused_bit = ((byte << 1) as i8) >> (64 - shift);
644                if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) {
645                    let msg = if continuation_bit {
646                        "invalid var_i64: integer representation too long"
647                    } else {
648                        "invalid var_i64: integer too large"
649                    };
650                    return Err(BinaryReaderError::new(msg, self.original_position() - 1));
651                }
652                return Ok(result);
653            }
654            shift += 7;
655            if (byte & 0x80) == 0 {
656                break;
657            }
658        }
659        let ashift = 64 - shift;
660        Ok((result << ashift) >> ashift)
661    }
662
663    /// Advances the `BinaryReader` four bytes to parse a 32 bit floating point
664    /// number, returned as `Ieee32`.
665    /// # Errors
666    /// If `BinaryReader` has less than four bytes remaining.
667    pub fn read_f32(&mut self) -> Result<Ieee32> {
668        let value = self.read_u32()?;
669        Ok(Ieee32(value))
670    }
671
672    /// Advances the `BinaryReader` eight bytes to parse a 64 bit floating point
673    /// number, returned as `Ieee64`.
674    /// # Errors
675    /// If `BinaryReader` has less than eight bytes remaining.
676    pub fn read_f64(&mut self) -> Result<Ieee64> {
677        let value = self.read_u64()?;
678        Ok(Ieee64(value))
679    }
680
681    /// (internal) Reads a fixed-size WebAssembly string from the module.
682    fn internal_read_string(&mut self, len: usize) -> Result<&'a str> {
683        let bytes = self.read_bytes(len)?;
684        str::from_utf8(bytes).map_err(|_| {
685            BinaryReaderError::new("malformed UTF-8 encoding", self.original_position() - 1)
686        })
687    }
688
689    /// Reads a WebAssembly string from the module.
690    ///
691    /// # Errors
692    ///
693    /// If `BinaryReader` has less than up to four bytes remaining, the string's
694    /// length exceeds the remaining bytes, the string's length exceeds
695    /// `limits::MAX_WASM_STRING_SIZE`, or the string contains invalid utf-8.
696    pub fn read_string(&mut self) -> Result<&'a str> {
697        let len = self.read_var_u32()? as usize;
698        if len > MAX_WASM_STRING_SIZE {
699            return Err(BinaryReaderError::new(
700                "string size out of bounds",
701                self.original_position() - 1,
702            ));
703        }
704        return self.internal_read_string(len);
705    }
706
707    /// Reads a unlimited WebAssembly string from the module.
708    ///
709    /// Note that this is similar to [`BinaryReader::read_string`] except that
710    /// it will not limit the size of the returned string by
711    /// `limits::MAX_WASM_STRING_SIZE`.
712    pub fn read_unlimited_string(&mut self) -> Result<&'a str> {
713        let len = self.read_var_u32()? as usize;
714        return self.internal_read_string(len);
715    }
716
717    #[cold]
718    pub(crate) fn invalid_leading_byte<T>(&self, byte: u8, desc: &str) -> Result<T> {
719        Err(Self::invalid_leading_byte_error(
720            byte,
721            desc,
722            self.original_position() - 1,
723        ))
724    }
725
726    pub(crate) fn invalid_leading_byte_error(
727        byte: u8,
728        desc: &str,
729        offset: usize,
730    ) -> BinaryReaderError {
731        format_err!(offset, "invalid leading byte (0x{byte:x}) for {desc}")
732    }
733
734    pub(crate) fn peek(&self) -> Result<u8> {
735        self.ensure_has_byte()?;
736        Ok(self.buffer[self.position])
737    }
738
739    pub(crate) fn read_block_type(&mut self) -> Result<BlockType> {
740        let b = self.peek()?;
741
742        // Block types are encoded as either 0x40, a `valtype`, or `s33`. All
743        // current `valtype` encodings are negative numbers when encoded with
744        // sleb128, but it's also required that valtype encodings are in their
745        // canonical form. For example an overlong encoding of -1 as `0xff 0x7f`
746        // is not valid and it is required to be `0x7f`. This means that we
747        // can't simply match on the `s33` that pops out below since reading the
748        // whole `s33` might read an overlong encoding.
749        //
750        // To test for this the first byte `b` is inspected. The highest bit,
751        // the continuation bit in LEB128 encoding, must be clear. The next bit,
752        // the sign bit, must be set to indicate that the number is negative. If
753        // these two conditions hold then we're guaranteed that this is a
754        // negative number.
755        //
756        // After this a value type is read directly instead of looking for an
757        // indexed value type.
758        if b & 0x80 == 0 && b & 0x40 != 0 {
759            if b == 0x40 {
760                self.position += 1;
761                return Ok(BlockType::Empty);
762            }
763            return Ok(BlockType::Type(self.read()?));
764        }
765
766        // Not empty or a singular type, so read the function type index
767        let idx = self.read_var_s33()?;
768        match u32::try_from(idx) {
769            Ok(idx) => Ok(BlockType::FuncType(idx)),
770            Err(_) => {
771                return Err(BinaryReaderError::new(
772                    "invalid function type",
773                    self.original_position(),
774                ));
775            }
776        }
777    }
778
779    /// Returns whether there is an `end` opcode followed by eof remaining in
780    /// this reader.
781    pub fn is_end_then_eof(&self) -> bool {
782        self.remaining_buffer() == &[0x0b]
783    }
784
785    pub(crate) fn read_header_version(&mut self) -> Result<u32> {
786        let magic_number = self.read_bytes(4)?;
787        if magic_number != WASM_MAGIC_NUMBER {
788            return Err(BinaryReaderError::new(
789                format!(
790                    "magic header not detected: bad magic number - expected={WASM_MAGIC_NUMBER:#x?} actual={magic_number:#x?}"
791                ),
792                self.original_position() - 4,
793            ));
794        }
795        self.read_u32()
796    }
797}
798
799// See documentation on `BinaryReader::features` for more on what's going on
800// here.
801macro_rules! define_feature_accessor {
802    ($feature:ident = $default:expr) => {
803        impl BinaryReader<'_> {
804            #[inline]
805            #[allow(dead_code)]
806            pub(crate) fn $feature(&self) -> bool {
807                #[cfg(feature = "features")]
808                {
809                    self.features.$feature()
810                }
811                #[cfg(not(feature = "features"))]
812                {
813                    true
814                }
815            }
816        }
817    };
818}
819
820super::features::foreach_wasm_feature!(define_feature_accessor);
821
822/// Iterator returned from [`BinaryReader::read_iter`].
823pub struct BinaryReaderIter<'a, 'me, T: FromReader<'a>> {
824    remaining: usize,
825    pub(crate) reader: &'me mut BinaryReader<'a>,
826    _marker: marker::PhantomData<T>,
827}
828
829impl<'a, T> Iterator for BinaryReaderIter<'a, '_, T>
830where
831    T: FromReader<'a>,
832{
833    type Item = Result<T>;
834
835    fn next(&mut self) -> Option<Result<T>> {
836        if self.remaining == 0 {
837            None
838        } else {
839            let ret = self.reader.read::<T>();
840            if ret.is_err() {
841                self.remaining = 0;
842            } else {
843                self.remaining -= 1;
844            }
845            Some(ret)
846        }
847    }
848
849    fn size_hint(&self) -> (usize, Option<usize>) {
850        (self.remaining, Some(self.remaining))
851    }
852}
853
854impl<'a, T> Drop for BinaryReaderIter<'a, '_, T>
855where
856    T: FromReader<'a>,
857{
858    fn drop(&mut self) {
859        while self.next().is_some() {
860            // ...
861        }
862    }
863}