1 #[cfg(feature = "coredump")]
2 use super::coredump::WasmCoreDump;
3 #[cfg(feature = "gc")]
4 use crate::ThrownException;
5 use crate::prelude::*;
6 use crate::store::StoreOpaque;
7 use crate::{AsContext, Module};
8 use core::fmt;
9 use wasmtime_environ::{FilePos, demangle_function_name, demangle_function_name_or_index};
10 
11 /// Representation of a WebAssembly trap and what caused it to occur.
12 ///
13 /// WebAssembly traps happen explicitly for instructions such as `unreachable`
14 /// but can also happen as side effects of other instructions such as `i32.load`
15 /// loading an out-of-bounds address. Traps halt the execution of WebAssembly
16 /// and cause an error to be returned to the host. This enumeration is a list of
17 /// all possible traps that can happen in wasm, in addition to some
18 /// Wasmtime-specific trap codes listed here as well.
19 ///
20 /// # Errors in Wasmtime
21 ///
22 /// Error-handling in Wasmtime is primarily done through the
23 /// [`wasmtime::Error`] type where most results are a
24 /// [`wasmtime::Result<T>`] which is an alias for [`Result<T,
25 /// wasmtime::Error>`](std::result::Result). Errors in Wasmtime are represented
26 /// with [`wasmtime::Error`] which acts as a container for any type of error in
27 /// addition to optional context for this error. The "base" error or
28 /// [`wasmtime::Error::root_cause`] is a [`Trap`] whenever WebAssembly hits a
29 /// trap, or otherwise it's whatever the host created the error with when
30 /// returning an error for a host call.
31 ///
32 /// Any error which happens while WebAssembly is executing will also, by
33 /// default, capture a backtrace of the wasm frames while executing. This
34 /// backtrace is represented with a [`WasmBacktrace`] instance and is attached
35 /// to the [`wasmtime::Error`] return value as a
36 /// [`context`](crate::Error::context). Inspecting a [`WasmBacktrace`] can be
37 /// done with the [`downcast_ref`](crate::Error::downcast_ref) function. For
38 /// information on this see the [`WasmBacktrace`] documentation.
39 ///
40 /// [`wasmtime::Error`]: crate::Error
41 /// [`wasmtime::Result<T>`]: crate::Result
42 /// [`wasmtime::Error::root_cause`]: crate::Error::root_cause
43 ///
44 /// # Examples
45 ///
46 /// ```
47 /// # use wasmtime::*;
48 /// # fn main() -> Result<()> {
49 /// let engine = Engine::default();
50 /// let module = Module::new(
51 ///     &engine,
52 ///     r#"
53 ///         (module
54 ///             (func (export "trap")
55 ///                 unreachable)
56 ///             (func $overflow (export "overflow")
57 ///                 call $overflow)
58 ///         )
59 ///     "#,
60 /// )?;
61 /// let mut store = Store::new(&engine, ());
62 /// let instance = Instance::new(&mut store, &module, &[])?;
63 ///
64 /// let trap = instance.get_typed_func::<(), ()>(&mut store, "trap")?;
65 /// let error = trap.call(&mut store, ()).unwrap_err();
66 /// assert_eq!(*error.downcast_ref::<Trap>().unwrap(), Trap::UnreachableCodeReached);
67 /// assert!(error.root_cause().is::<Trap>());
68 ///
69 /// let overflow = instance.get_typed_func::<(), ()>(&mut store, "overflow")?;
70 /// let error = overflow.call(&mut store, ()).unwrap_err();
71 /// assert_eq!(*error.downcast_ref::<Trap>().unwrap(), Trap::StackOverflow);
72 /// # Ok(())
73 /// # }
74 /// ```
75 pub use wasmtime_environ::Trap;
76 
77 #[cold] // traps are exceptional, this helps move handling off the main path
78 pub(crate) fn from_runtime_box(
79     store: &mut StoreOpaque,
80     runtime_trap: Box<crate::runtime::vm::Trap>,
81 ) -> Error {
82     let crate::runtime::vm::Trap {
83         reason,
84         backtrace,
85         coredumpstack,
86     } = *runtime_trap;
87     let (mut error, pc) = match reason {
88         #[cfg(feature = "gc")]
89         crate::runtime::vm::TrapReason::Exception => (ThrownException.into(), None),
90         // For user-defined errors they're already an `crate::Error` so no
91         // conversion is really necessary here, but a `backtrace` may have
92         // been captured so it's attempted to get inserted here.
93         //
94         // If the error is actually a `Trap` then the backtrace is inserted
95         // directly into the `Trap` since there's storage there for it.
96         // Otherwise though this represents a host-defined error which isn't
97         // using a `Trap` but instead some other condition that was fatal to
98         // wasm itself. In that situation the backtrace is inserted as
99         // contextual information on error using `error.context(...)` to
100         // provide useful information to debug with for the embedder/caller,
101         // otherwise the information about what the wasm was doing when the
102         // error was generated would be lost.
103         crate::runtime::vm::TrapReason::User(error) => (error, None),
104         crate::runtime::vm::TrapReason::Jit {
105             pc,
106             faulting_addr,
107             trap,
108         } => {
109             let mut err: Error = trap.into();
110 
111             // If a fault address was present, for example with segfaults,
112             // then simultaneously assert that it's within a known linear memory
113             // and additionally translate it to a wasm-local address to be added
114             // as context to the error.
115             if let Some(fault) = faulting_addr.and_then(|addr| store.wasm_fault(pc, addr)) {
116                 err = err.context(fault);
117             }
118             (err, Some(pc))
119         }
120         crate::runtime::vm::TrapReason::Wasm(trap_code) => (trap_code.into(), None),
121     };
122 
123     if let Some(bt) = backtrace {
124         let bt = WasmBacktrace::from_captured(store, bt, pc);
125         if !bt.wasm_trace.is_empty() {
126             error = error.context(bt);
127         }
128     }
129 
130     let _ = &coredumpstack;
131     #[cfg(feature = "coredump")]
132     if let Some(coredump) = coredumpstack {
133         let bt = WasmBacktrace::from_captured(store, coredump.bt, pc);
134         let cd = WasmCoreDump::new(store, bt);
135         error = error.context(cd);
136     }
137 
138     error
139 }
140 
141 /// Representation of a backtrace of function frames in a WebAssembly module for
142 /// where an error happened.
143 ///
144 /// This structure is attached to the [`wasmtime::Error`] returned from many
145 /// Wasmtime functions that execute WebAssembly such as [`Instance::new`] or
146 /// [`Func::call`]. This can be acquired with the
147 /// [`Error::downcast`](crate::Error::downcast) family of methods to
148 /// programmatically inspect the backtrace. Otherwise since it's part of the
149 /// error returned this will get printed along with the rest of the error when
150 /// the error is logged.
151 ///
152 /// Capturing of wasm backtraces can be configured through the
153 /// [`Config::wasm_backtrace`](crate::Config::wasm_backtrace) method.
154 ///
155 /// For more information about errors in wasmtime see the documentation of the
156 /// [`Trap`] type.
157 ///
158 /// [`Func::call`]: crate::Func::call
159 /// [`Instance::new`]: crate::Instance::new
160 /// [`wasmtime::Error`]: crate::Error
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// # use wasmtime::*;
166 /// # fn main() -> Result<()> {
167 /// let engine = Engine::default();
168 /// let module = Module::new(
169 ///     &engine,
170 ///     r#"
171 ///         (module
172 ///             (func $start (export "run")
173 ///                 call $trap)
174 ///             (func $trap
175 ///                 unreachable)
176 ///         )
177 ///     "#,
178 /// )?;
179 /// let mut store = Store::new(&engine, ());
180 /// let instance = Instance::new(&mut store, &module, &[])?;
181 /// let func = instance.get_typed_func::<(), ()>(&mut store, "run")?;
182 /// let error = func.call(&mut store, ()).unwrap_err();
183 /// let bt = error.downcast_ref::<WasmBacktrace>().unwrap();
184 /// let frames = bt.frames();
185 /// assert_eq!(frames.len(), 2);
186 /// assert_eq!(frames[0].func_name(), Some("trap"));
187 /// assert_eq!(frames[1].func_name(), Some("start"));
188 /// # Ok(())
189 /// # }
190 /// ```
191 #[derive(Debug)]
192 pub struct WasmBacktrace {
193     wasm_trace: Vec<FrameInfo>,
194     hint_wasm_backtrace_details_env: bool,
195     // This is currently only present for the `Debug` implementation for extra
196     // context.
197     _runtime_trace: crate::runtime::vm::Backtrace,
198 }
199 
200 impl WasmBacktrace {
201     /// Captures a trace of the WebAssembly frames on the stack for the
202     /// provided store.
203     ///
204     /// This will return a [`WasmBacktrace`] which holds captured
205     /// [`FrameInfo`]s for each frame of WebAssembly on the call stack of the
206     /// current thread. If no WebAssembly is on the stack then the returned
207     /// backtrace will have no frames in it.
208     ///
209     /// Note that this function will respect the [`Config::wasm_backtrace`]
210     /// configuration option and will return an empty backtrace if that is
211     /// disabled. To always capture a backtrace use the
212     /// [`WasmBacktrace::force_capture`] method.
213     ///
214     /// Also note that this function will only capture frames from the
215     /// specified `store` on the stack, ignoring frames from other stores if
216     /// present.
217     ///
218     /// [`Config::wasm_backtrace`]: crate::Config::wasm_backtrace
219     ///
220     /// # Example
221     ///
222     /// ```
223     /// # use wasmtime::*;
224     /// # fn main() -> Result<()> {
225     /// let engine = Engine::default();
226     /// let module = Module::new(
227     ///     &engine,
228     ///     r#"
229     ///         (module
230     ///             (import "" "" (func $host))
231     ///             (func $foo (export "f") call $bar)
232     ///             (func $bar call $host)
233     ///         )
234     ///     "#,
235     /// )?;
236     ///
237     /// let mut store = Store::new(&engine, ());
238     /// let func = Func::wrap(&mut store, |cx: Caller<'_, ()>| {
239     ///     let trace = WasmBacktrace::capture(&cx);
240     ///     println!("{trace:?}");
241     /// });
242     /// let instance = Instance::new(&mut store, &module, &[func.into()])?;
243     /// let func = instance.get_typed_func::<(), ()>(&mut store, "f")?;
244     /// func.call(&mut store, ())?;
245     /// # Ok(())
246     /// # }
247     /// ```
248     pub fn capture(store: impl AsContext) -> WasmBacktrace {
249         let store = store.as_context();
250         if store.engine().config().wasm_backtrace {
251             Self::force_capture(store)
252         } else {
253             WasmBacktrace {
254                 wasm_trace: Vec::new(),
255                 hint_wasm_backtrace_details_env: false,
256                 _runtime_trace: crate::runtime::vm::Backtrace::empty(),
257             }
258         }
259     }
260 
261     /// Unconditionally captures a trace of the WebAssembly frames on the stack
262     /// for the provided store.
263     ///
264     /// Same as [`WasmBacktrace::capture`] except that it disregards the
265     /// [`Config::wasm_backtrace`](crate::Config::wasm_backtrace) setting and
266     /// always captures a backtrace.
267     pub fn force_capture(store: impl AsContext) -> WasmBacktrace {
268         let store = store.as_context();
269         Self::from_captured(store.0, crate::runtime::vm::Backtrace::new(store.0), None)
270     }
271 
272     fn from_captured(
273         store: &StoreOpaque,
274         runtime_trace: crate::runtime::vm::Backtrace,
275         trap_pc: Option<usize>,
276     ) -> Self {
277         let mut wasm_trace = Vec::<FrameInfo>::with_capacity(runtime_trace.frames().len());
278         let mut hint_wasm_backtrace_details_env = false;
279         let wasm_backtrace_details_env_used =
280             store.engine().config().wasm_backtrace_details_env_used;
281 
282         for frame in runtime_trace.frames() {
283             debug_assert!(frame.pc() != 0);
284 
285             // Note that we need to be careful about the pc we pass in
286             // here to lookup frame information. This program counter is
287             // used to translate back to an original source location in
288             // the origin wasm module. If this pc is the exact pc that
289             // the trap happened at, then we look up that pc precisely.
290             // Otherwise backtrace information typically points at the
291             // pc *after* the call instruction (because otherwise it's
292             // likely a call instruction on the stack). In that case we
293             // want to lookup information for the previous instruction
294             // (the call instruction) so we subtract one as the lookup.
295             let pc_to_lookup = if Some(frame.pc()) == trap_pc {
296                 frame.pc()
297             } else {
298                 frame.pc() - 1
299             };
300 
301             // NB: The PC we are looking up _must_ be a Wasm PC since
302             // `crate::runtime::vm::Backtrace` only contains Wasm frames.
303             //
304             // However, consider the case where we have multiple, nested calls
305             // across stores (with host code in between, by necessity, since
306             // only things in the same store can be linked directly together):
307             //
308             //     | ...             |
309             //     | Host            |  |
310             //     +-----------------+  | stack
311             //     | Wasm in store A |  | grows
312             //     +-----------------+  | down
313             //     | Host            |  |
314             //     +-----------------+  |
315             //     | Wasm in store B |  V
316             //     +-----------------+
317             //
318             // In this scenario, the `crate::runtime::vm::Backtrace` will
319             // contain two frames: Wasm in store B followed by Wasm in store
320             // A. But `store.modules()` will only have the module information
321             // for modules instantiated within this store. Therefore, we use `if
322             // let Some(..)` instead of the `unwrap` you might otherwise expect
323             // and we ignore frames from modules that were not registered in
324             // this store's module registry.
325             if let Some((info, module)) = store.modules().lookup_frame_info(pc_to_lookup) {
326                 wasm_trace.push(info);
327 
328                 // If this frame has unparsed debug information and the
329                 // store's configuration indicates that we were
330                 // respecting the environment variable of whether to
331                 // do this then we will print out a helpful note in
332                 // `Display` to indicate that more detailed information
333                 // in a trap may be available.
334                 let has_unparsed_debuginfo =
335                     module.module().compiled_module().has_unparsed_debuginfo();
336                 if has_unparsed_debuginfo
337                     && wasm_backtrace_details_env_used
338                     && cfg!(feature = "addr2line")
339                 {
340                     hint_wasm_backtrace_details_env = true;
341                 }
342             }
343         }
344 
345         Self {
346             wasm_trace,
347             _runtime_trace: runtime_trace,
348             hint_wasm_backtrace_details_env,
349         }
350     }
351 
352     /// Returns a list of function frames in WebAssembly this backtrace
353     /// represents.
354     pub fn frames(&self) -> &[FrameInfo] {
355         self.wasm_trace.as_slice()
356     }
357 }
358 
359 impl fmt::Display for WasmBacktrace {
360     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361         writeln!(f, "error while executing at wasm backtrace:")?;
362 
363         let mut needs_newline = false;
364         for (i, frame) in self.wasm_trace.iter().enumerate() {
365             // Avoid putting a trailing newline on the output
366             if needs_newline {
367                 writeln!(f, "")?;
368             } else {
369                 needs_newline = true;
370             }
371             let name = frame.module().name().unwrap_or("<unknown>");
372             write!(f, "  {i:>3}: ")?;
373 
374             if let Some(offset) = frame.module_offset() {
375                 write!(f, "{offset:#8x} - ")?;
376             }
377 
378             let write_raw_func_name = |f: &mut fmt::Formatter<'_>| {
379                 demangle_function_name_or_index(f, frame.func_name(), frame.func_index() as usize)
380             };
381             if frame.symbols().is_empty() {
382                 write!(f, "{name}!")?;
383                 write_raw_func_name(f)?;
384             } else {
385                 for (i, symbol) in frame.symbols().iter().enumerate() {
386                     if i > 0 {
387                         if needs_newline {
388                             writeln!(f, "")?;
389                         } else {
390                             needs_newline = true;
391                         }
392                         write!(f, "                - ")?;
393                     } else {
394                         // ...
395                     }
396                     match symbol.name() {
397                         Some(name) => demangle_function_name(f, name)?,
398                         None if i == 0 => write_raw_func_name(f)?,
399                         None => write!(f, "<inlined function>")?,
400                     }
401                     if let Some(file) = symbol.file() {
402                         writeln!(f, "")?;
403                         write!(f, "                    at {file}")?;
404                         if let Some(line) = symbol.line() {
405                             write!(f, ":{line}")?;
406                             if let Some(col) = symbol.column() {
407                                 write!(f, ":{col}")?;
408                             }
409                         }
410                     }
411                 }
412             }
413         }
414         if self.hint_wasm_backtrace_details_env {
415             write!(
416                 f,
417                 "\nnote: using the `WASMTIME_BACKTRACE_DETAILS=1` \
418                  environment variable may show more debugging information"
419             )?;
420         }
421         Ok(())
422     }
423 }
424 
425 /// Description of a frame in a backtrace for a [`WasmBacktrace`].
426 ///
427 /// Whenever an error happens while WebAssembly is executing a
428 /// [`WasmBacktrace`] will be attached to the error returned which can be used
429 /// to acquire this `FrameInfo`. For more information see [`WasmBacktrace`].
430 #[derive(Debug)]
431 pub struct FrameInfo {
432     module: Module,
433     func_index: u32,
434     func_name: Option<String>,
435     func_start: FilePos,
436     instr: Option<FilePos>,
437     symbols: Vec<FrameSymbol>,
438 }
439 
440 impl FrameInfo {
441     /// Fetches frame information about a program counter in a backtrace.
442     ///
443     /// Returns an object if this `pc` is known to this module, or returns `None`
444     /// if no information can be found.
445     pub(crate) fn new(module: Module, text_offset: usize) -> Option<FrameInfo> {
446         let compiled_module = module.compiled_module();
447         let index = compiled_module.func_by_text_offset(text_offset)?;
448         let func_start = compiled_module.func_start_srcloc(index);
449         let instr =
450             wasmtime_environ::lookup_file_pos(module.engine_code().address_map_data(), text_offset);
451         let index = compiled_module.module().func_index(index);
452         let func_index = index.as_u32();
453         let func_name = compiled_module.func_name(index).map(|s| s.to_string());
454 
455         // In debug mode for now assert that we found a mapping for `pc` within
456         // the function, because otherwise something is buggy along the way and
457         // not accounting for all the instructions. This isn't super critical
458         // though so we can omit this check in release mode.
459         //
460         // Note that if the module doesn't even have an address map due to
461         // compilation settings then it's expected that `instr` is `None`.
462         debug_assert!(
463             instr.is_some() || !compiled_module.has_address_map(),
464             "failed to find instruction for {text_offset:#x}"
465         );
466 
467         // Use our wasm-relative pc to symbolize this frame. If there's a
468         // symbolication context (dwarf debug info) available then we can try to
469         // look this up there.
470         //
471         // Note that dwarf pcs are code-section-relative, hence the subtraction
472         // from the location of `instr`. Also note that all errors are ignored
473         // here for now since technically wasm modules can always have any
474         // custom section contents.
475         let mut symbols = Vec::new();
476 
477         let _ = &mut symbols;
478         #[cfg(feature = "addr2line")]
479         if let Some(s) = &compiled_module.symbolize_context().ok().and_then(|c| c) {
480             if let Some(offset) = instr.and_then(|i| i.file_offset()) {
481                 let to_lookup = u64::from(offset) - s.code_section_offset();
482                 if let Ok(mut frames) = s.addr2line().find_frames(to_lookup).skip_all_loads() {
483                     while let Ok(Some(frame)) = frames.next() {
484                         symbols.push(FrameSymbol {
485                             name: frame
486                                 .function
487                                 .as_ref()
488                                 .and_then(|l| l.raw_name().ok())
489                                 .map(|s| s.to_string()),
490                             file: frame
491                                 .location
492                                 .as_ref()
493                                 .and_then(|l| l.file)
494                                 .map(|s| s.to_string()),
495                             line: frame.location.as_ref().and_then(|l| l.line),
496                             column: frame.location.as_ref().and_then(|l| l.column),
497                         });
498                     }
499                 }
500             }
501         }
502 
503         Some(FrameInfo {
504             module,
505             func_index,
506             func_name,
507             instr,
508             func_start,
509             symbols,
510         })
511     }
512 
513     /// Returns the WebAssembly function index for this frame.
514     ///
515     /// This function index is the index in the function index space of the
516     /// WebAssembly module that this frame comes from.
517     pub fn func_index(&self) -> u32 {
518         self.func_index
519     }
520 
521     /// Returns the module for this frame.
522     ///
523     /// This is the module who's code was being run in this frame.
524     pub fn module(&self) -> &Module {
525         &self.module
526     }
527 
528     /// Returns a descriptive name of the function for this frame, if one is
529     /// available.
530     ///
531     /// The name of this function may come from the `name` section of the
532     /// WebAssembly binary, or wasmtime may try to infer a better name for it if
533     /// not available, for example the name of the export if it's exported.
534     ///
535     /// This return value is primarily used for debugging and human-readable
536     /// purposes for things like traps. Note that the exact return value may be
537     /// tweaked over time here and isn't guaranteed to be something in
538     /// particular about a wasm module due to its primary purpose of assisting
539     /// in debugging.
540     ///
541     /// This function returns `None` when no name could be inferred.
542     pub fn func_name(&self) -> Option<&str> {
543         self.func_name.as_deref()
544     }
545 
546     /// Returns the offset within the original wasm module this frame's program
547     /// counter was at.
548     ///
549     /// The offset here is the offset from the beginning of the original wasm
550     /// module to the instruction that this frame points to.
551     ///
552     /// Note that `None` may be returned if the original module was not
553     /// compiled with mapping information to yield this information. This is
554     /// controlled by the
555     /// [`Config::generate_address_map`](crate::Config::generate_address_map)
556     /// configuration option.
557     pub fn module_offset(&self) -> Option<usize> {
558         Some(self.instr?.file_offset()? as usize)
559     }
560 
561     /// Returns the offset from the original wasm module's function to this
562     /// frame's program counter.
563     ///
564     /// The offset here is the offset from the beginning of the defining
565     /// function of this frame (within the wasm module) to the instruction this
566     /// frame points to.
567     ///
568     /// Note that `None` may be returned if the original module was not
569     /// compiled with mapping information to yield this information. This is
570     /// controlled by the
571     /// [`Config::generate_address_map`](crate::Config::generate_address_map)
572     /// configuration option.
573     pub fn func_offset(&self) -> Option<usize> {
574         let instr_offset = self.instr?.file_offset()?;
575         Some((instr_offset - self.func_start.file_offset()?) as usize)
576     }
577 
578     /// Returns the debug symbols found, if any, for this function frame.
579     ///
580     /// When a wasm program is compiled with DWARF debug information then this
581     /// function may be populated to return symbols which contain extra debug
582     /// information about a frame including the filename and line number. If no
583     /// debug information was found or if it was malformed then this will return
584     /// an empty array.
585     pub fn symbols(&self) -> &[FrameSymbol] {
586         &self.symbols
587     }
588 }
589 
590 /// Debug information for a symbol that is attached to a [`FrameInfo`].
591 ///
592 /// When DWARF debug information is present in a wasm file then this structure
593 /// can be found on a [`FrameInfo`] and can be used to learn about filenames,
594 /// line numbers, etc, which are the origin of a function in a stack trace.
595 #[derive(Debug)]
596 pub struct FrameSymbol {
597     name: Option<String>,
598     file: Option<String>,
599     line: Option<u32>,
600     column: Option<u32>,
601 }
602 
603 impl FrameSymbol {
604     /// Returns the function name associated with this symbol.
605     ///
606     /// Note that this may not be present with malformed debug information, or
607     /// the debug information may not include it. Also note that the symbol is
608     /// frequently mangled, so you might need to run some form of demangling
609     /// over it.
610     pub fn name(&self) -> Option<&str> {
611         self.name.as_deref()
612     }
613 
614     /// Returns the source code filename this symbol was defined in.
615     ///
616     /// Note that this may not be present with malformed debug information, or
617     /// the debug information may not include it.
618     pub fn file(&self) -> Option<&str> {
619         self.file.as_deref()
620     }
621 
622     /// Returns the 1-indexed source code line number this symbol was defined
623     /// on.
624     ///
625     /// Note that this may not be present with malformed debug information, or
626     /// the debug information may not include it.
627     pub fn line(&self) -> Option<u32> {
628         self.line
629     }
630 
631     /// Returns the 1-indexed source code column number this symbol was defined
632     /// on.
633     ///
634     /// Note that this may not be present with malformed debug information, or
635     /// the debug information may not include it.
636     pub fn column(&self) -> Option<u32> {
637         self.column
638     }
639 }
640