1 use crate::prelude::*;
2 use 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.
26 macro_rules! bail_bug {
27     ($($arg:tt)*) => {{
28         // Minimize argument passing to the `new` function by placing the
29         // file/line in a static which is passed by reference to just pass a
30         // single extra pointer argument.
31         static POS: (&'static str, u32) = (file!(), line!());
32         $crate::bail!(crate::WasmtimeBug::new(format_args!($($arg)*), &POS))
33     }}
34 }
35 
36 pub(crate) use bail_bug;
37 
38 /// Error which indicates a bug in Wasmtime.
39 ///
40 /// This structure is used internally with Wasmtime for situations which are a
41 /// bug in Wasmtime but not serious enough to raise a panic and unwind the
42 /// current thread of execution. In these situations this is still considered a
43 /// bug and a trap is raised to terminate a guest, and it's considered something
44 /// that needs to be fixed in Wasmtime.
45 #[derive(Debug)]
46 pub struct WasmtimeBug {
47     message: String,
48     file: &'static str,
49     line: u32,
50 }
51 
52 impl WasmtimeBug {
53     #[cold]
new(message: fmt::Arguments<'_>, pos: &'static (&'static str, u32)) -> Self54     pub(crate) fn new(message: fmt::Arguments<'_>, pos: &'static (&'static str, u32)) -> Self {
55         if cfg!(debug_assertions) {
56             panic!("BUG: {message}");
57         }
58         Self {
59             message: message.to_string(),
60             file: pos.0,
61             line: pos.1,
62         }
63     }
64 }
65 
66 impl fmt::Display for WasmtimeBug {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result67     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68         write!(
69             f,
70             "\
71 BUG: {}
72 location: {}:{}
73 version: {}
74 
75 This is a bug in Wasmtime that was not thought to be reachable. A panic is
76 not happening to avoid taking down the thread, but this trap is being injected
77 into WebAssembly guests to prevent their execution. The Wasmtime project would
78 appreciate a bug report with a copy of this message to help investigate what
79 happened. If you're able to provide a reproduction, that would be appreciated,
80 but it is not necessary to do so and instead indicating that this is reachable
81 is a sufficiently actionable bug for maintainers to investigate.
82 
83 ",
84             self.message,
85             self.file,
86             self.line,
87             env!("CARGO_PKG_VERSION"),
88         )
89     }
90 }
91 
92 impl core::error::Error for WasmtimeBug {}
93