wasmtime/runtime/bug.rs
1use crate::prelude::*;
2use core::fmt;
3
4/// Helper macro to `bail!` with a `WasmtimeBug` instance.
5///
6/// This is used in locations in lieu of panicking. The general idea when using
7/// this is:
8///
9/// * The invocation of this cannot be refactored to be statically ruled out.
10/// * The invocation cannot be reasoned about locally to determine that this is
11/// dynamically not reachable.
12///
13/// This macro serves as an alternative to `panic!` which returns a
14/// `WasmtimeBug` instead of panicking. This means that a trap is raised in the
15/// guest and a store is poisoned for example (w.r.t. components). This
16/// primarily serves as a DoS mitigation mechanism where if the panic were
17/// actually hit at runtime it would be a CVE. The worst-case scenario of
18/// raising a trap is that a guest is erroneously terminated, which is a much
19/// more controlled failure mode.
20///
21/// The general guideline for using this is "don't" if you can avoid it because
22/// it's best to either statically rule out these cases or make it verifiable
23/// locally that it can't be hit. When this isn't possible, however, this is a
24/// good alternative to panicking in the case that this is actually executed at
25/// runtime.
26macro_rules! bail_bug {
27 ($($arg:tt)*) => {{
28 $crate::bail!($crate::bug!($($arg)*))
29 }}
30}
31pub(crate) use bail_bug;
32
33/// Similar to `bail_bug`, but just returns a `WasmtimeBug`.
34macro_rules! bug {
35 ($($arg:tt)*) => {{
36 // Minimize argument passing to the `new` function by placing the
37 // file/line in a static which is passed by reference to just pass a
38 // single extra pointer argument.
39 static POS: (&'static str, u32) = (file!(), line!());
40 crate::WasmtimeBug::new(format_args!($($arg)*), &POS)
41 }}
42}
43pub(crate) use bug;
44
45/// Error which indicates a bug in Wasmtime.
46///
47/// This structure is used internally with Wasmtime for situations which are a
48/// bug in Wasmtime but not serious enough to raise a panic and unwind the
49/// current thread of execution. In these situations this is still considered a
50/// bug and a trap is raised to terminate a guest, and it's considered something
51/// that needs to be fixed in Wasmtime.
52#[derive(Debug)]
53pub struct WasmtimeBug {
54 message: String,
55 file: &'static str,
56 line: u32,
57}
58
59impl WasmtimeBug {
60 #[cold]
61 pub(crate) fn new(message: fmt::Arguments<'_>, pos: &'static (&'static str, u32)) -> Self {
62 if cfg!(debug_assertions) {
63 panic!("BUG: {message}");
64 }
65 Self {
66 message: message.to_string(),
67 file: pos.0,
68 line: pos.1,
69 }
70 }
71}
72
73impl fmt::Display for WasmtimeBug {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(
76 f,
77 "\
78BUG: {}
79location: {}:{}
80version: {}
81
82This is a bug in Wasmtime that was not thought to be reachable. A panic is
83not happening to avoid taking down the thread, but this trap is being injected
84into WebAssembly guests to prevent their execution. The Wasmtime project would
85appreciate a bug report with a copy of this message to help investigate what
86happened. If you're able to provide a reproduction, that would be appreciated,
87but it is not necessary to do so and instead indicating that this is reachable
88is a sufficiently actionable bug for maintainers to investigate.
89
90",
91 self.message,
92 self.file,
93 self.line,
94 env!("CARGO_PKG_VERSION"),
95 )
96 }
97}
98
99impl core::error::Error for WasmtimeBug {}