lucet_runtime_internals/instance/
state.rs

1use crate::instance::siginfo_ext::SiginfoExt;
2use crate::instance::{FaultDetails, TerminationDetails, YieldedVal};
3use crate::sysdeps::UContext;
4use libc::{SIGBUS, SIGSEGV};
5use std::any::Any;
6use std::ffi::{CStr, CString};
7
8/// The representation of a Lucet instance's state machine.
9pub enum State {
10    /// The instance is ready to run.
11    ///
12    /// Transitions to `Running` when the instance is run, or to `Ready` when it's reset.
13    Ready,
14
15    /// The instance is running.
16    ///
17    /// Transitions to `Ready` when the guest function returns normally, or to `Faulted`,
18    /// `Terminating`, or `Yielding` if the instance faults, terminates, or yields.
19    Running,
20
21    /// The instance has faulted, potentially fatally.
22    ///
23    /// Transitions to `Faulted` when filling in additional fault details, to `Running` if
24    /// re-running a non-fatally faulted instance, or to `Ready` when the instance is reset.
25    Faulted {
26        details: FaultDetails,
27        siginfo: libc::siginfo_t,
28        context: UContext,
29    },
30
31    /// The instance is in the process of terminating.
32    ///
33    /// Transitions only to `Terminated`; the `TerminationDetails` are always extracted into a
34    /// `RunResult` before anything else happens to the instance.
35    Terminating { details: TerminationDetails },
36
37    /// The instance has terminated, and must be reset before running again.
38    ///
39    /// Transitions to `Ready` if the instance is reset.
40    Terminated,
41
42    /// The instance is in the process of yielding.
43    ///
44    /// Transitions only to `Yielded`; the `YieldedVal` is always extracted into a
45    /// `RunResult` before anything else happens to the instance.
46    Yielding {
47        val: YieldedVal,
48        /// A phantom value carrying the type of the expected resumption value.
49        ///
50        /// Concretely, this should only ever be `Box<PhantomData<R>>` where `R` is the type
51        /// the guest expects upon resumption.
52        expecting: Box<dyn Any>,
53    },
54
55    /// The instance has yielded.
56    ///
57    /// Transitions to `Running` if the instance is resumed, or to `Ready` if the instance is reset.
58    Yielded {
59        /// A phantom value carrying the type of the expected resumption value.
60        ///
61        /// Concretely, this should only ever be `Box<PhantomData<R>>` where `R` is the type
62        /// the guest expects upon resumption.
63        expecting: Box<dyn Any>,
64    },
65
66    /// A placeholder state used with `std::mem::replace()` when a new state must be constructed by
67    /// moving values out of an old state.
68    ///
69    /// This is used so that we do not need a `Clone` impl for this type, which would add
70    /// unnecessary constraints to the types of values instances could yield or terminate with.
71    ///
72    /// It is an error for this state to appear outside of a transition between other states.
73    Transitioning,
74}
75
76impl std::fmt::Display for State {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            State::Ready => write!(f, "ready"),
80            State::Running => write!(f, "running"),
81            State::Faulted {
82                details, siginfo, ..
83            } => {
84                write!(f, "{}", details)?;
85                write!(
86                    f,
87                    " triggered by {}: ",
88                    strsignal_wrapper(siginfo.si_signo)
89                        .into_string()
90                        .expect("strsignal returns valid UTF-8")
91                )?;
92
93                if siginfo.si_signo == SIGSEGV || siginfo.si_signo == SIGBUS {
94                    // We know this is inside the heap guard, because by the time we get here,
95                    // `lucet_error_verify_trap_safety` will have run and validated it.
96                    write!(
97                        f,
98                        " accessed memory at {:p} (inside heap guard)",
99                        siginfo.si_addr_ext()
100                    )?;
101                }
102                Ok(())
103            }
104            State::Terminated { .. } => write!(f, "terminated"),
105            State::Terminating { .. } => write!(f, "terminating"),
106            State::Yielding { .. } => write!(f, "yielding"),
107            State::Yielded { .. } => write!(f, "yielded"),
108            State::Transitioning { .. } => {
109                write!(f, "transitioning (IF YOU SEE THIS, THERE'S PROBABLY A BUG)")
110            }
111        }
112    }
113}
114
115impl State {
116    pub fn is_ready(&self) -> bool {
117        if let State::Ready { .. } = self {
118            true
119        } else {
120            false
121        }
122    }
123
124    pub fn is_running(&self) -> bool {
125        if let State::Running = self {
126            true
127        } else {
128            false
129        }
130    }
131
132    pub fn is_faulted(&self) -> bool {
133        if let State::Faulted { .. } = self {
134            true
135        } else {
136            false
137        }
138    }
139
140    pub fn is_fatal(&self) -> bool {
141        if let State::Faulted {
142            details: FaultDetails { fatal, .. },
143            ..
144        } = self
145        {
146            *fatal
147        } else {
148            false
149        }
150    }
151
152    pub fn is_terminated(&self) -> bool {
153        if let State::Terminated { .. } = self {
154            true
155        } else {
156            false
157        }
158    }
159
160    pub fn is_yielded(&self) -> bool {
161        if let State::Yielded { .. } = self {
162            true
163        } else {
164            false
165        }
166    }
167}
168
169// TODO: PR into `libc`
170extern "C" {
171    #[no_mangle]
172    fn strsignal(sig: libc::c_int) -> *mut libc::c_char;
173}
174
175// TODO: PR into `nix`
176fn strsignal_wrapper(sig: libc::c_int) -> CString {
177    unsafe { CStr::from_ptr(strsignal(sig)).to_owned() }
178}