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}