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