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