Skip to main content

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 {}