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