Skip to content

Commit 9cea057

Browse files
committed
Allow configuring the signal number of the signal used to interrupt a sandbox
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 15786fe commit 9cea057

File tree

7 files changed

+92
-9
lines changed

7 files changed

+92
-9
lines changed

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ impl HypervLinuxDriver {
400400
cancel_requested: AtomicBool::new(false),
401401
tid: AtomicU64::new(unsafe { libc::pthread_self() }),
402402
retry_delay: config.get_interrupt_retry_delay(),
403+
sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
403404
dropped: AtomicBool::new(false),
404405
}),
405406

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ impl KVMDriver {
356356
tid: AtomicU64::new(unsafe { libc::pthread_self() }),
357357
retry_delay: config.get_interrupt_retry_delay(),
358358
dropped: AtomicBool::new(false),
359+
sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
359360
}),
360361

361362
#[cfg(gdb)]

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ use tracing::{instrument, Span};
2020
use crate::error::HyperlightError::ExecutionCanceledByHost;
2121
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
2222
use crate::metrics::METRIC_GUEST_CANCELLATION;
23-
#[cfg(any(kvm, mshv))]
24-
use crate::signal_handlers::INTERRUPT_VCPU_SIGRTMIN_OFFSET;
2523
use crate::{log_then_return, new_error, HyperlightError, Result};
2624

2725
/// Util for handling x87 fpu state
@@ -357,14 +355,16 @@ pub(super) struct LinuxInterruptHandle {
357355
dropped: AtomicBool,
358356
/// Retry delay between signals sent to the vcpu thread
359357
retry_delay: Duration,
358+
/// The offset of the SIGRTMIN signal used to interrupt the vcpu thread
359+
sig_rt_min_offset: u8,
360360
}
361361

362362
#[cfg(any(kvm, mshv))]
363363
impl InterruptHandle for LinuxInterruptHandle {
364364
fn kill(&self) -> bool {
365365
self.cancel_requested.store(true, Ordering::Relaxed);
366366

367-
let signal_number = libc::SIGRTMIN() + INTERRUPT_VCPU_SIGRTMIN_OFFSET;
367+
let signal_number = libc::SIGRTMIN() + self.sig_rt_min_offset as libc::c_int;
368368
let mut sent_signal = false;
369369

370370
while self.running.load(Ordering::Relaxed) {

src/hyperlight_host/src/sandbox/config.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
use std::cmp::max;
1818
use std::time::Duration;
1919

20+
use libc::c_int;
2021
use tracing::{instrument, Span};
2122

2223
use crate::mem::exe::ExeInfo;
@@ -63,6 +64,14 @@ pub struct SandboxConfiguration {
6364
/// signal can be delivered to the thread, but the thread may not yet
6465
/// have entered kernel space.
6566
interrupt_retry_delay: Duration,
67+
/// Offset from `SIGRTMIN` used to determine the signal number for interrupting
68+
/// the VCPU thread. The actual signal sent is `SIGRTMIN + interrupt_vcpu_sigrtmin_offset`.
69+
///
70+
/// This signal must fall within the valid real-time signal range supported by the host.
71+
///
72+
/// Note: Since real-time signals can vary across platforms, ensure that the offset
73+
/// results in a signal number that is not already in use by other components of the system.
74+
interrupt_vcpu_sigrtmin_offset: u8,
6675
}
6776

6877
impl SandboxConfiguration {
@@ -76,6 +85,8 @@ impl SandboxConfiguration {
7685
pub const MIN_OUTPUT_SIZE: usize = 0x2000;
7786
/// The default interrupt retry delay
7887
pub const DEFAULT_INTERRUPT_RETRY_DELAY: Duration = Duration::from_micros(500);
88+
/// The default signal offset from `SIGRTMIN` used to determine the signal number for interrupting
89+
pub const INTERRUPT_VCPU_SIGRTMIN_OFFSET: u8 = 0;
7990

8091
#[allow(clippy::too_many_arguments)]
8192
/// Create a new configuration for a sandbox with the given sizes.
@@ -86,6 +97,7 @@ impl SandboxConfiguration {
8697
stack_size_override: Option<u64>,
8798
heap_size_override: Option<u64>,
8899
interrupt_retry_delay: Duration,
100+
interrupt_vcpu_sigrtmin_offset: u8,
89101
#[cfg(gdb)] guest_debug_info: Option<DebugInfo>,
90102
) -> Self {
91103
Self {
@@ -94,7 +106,7 @@ impl SandboxConfiguration {
94106
stack_size_override: stack_size_override.unwrap_or(0),
95107
heap_size_override: heap_size_override.unwrap_or(0),
96108
interrupt_retry_delay,
97-
109+
interrupt_vcpu_sigrtmin_offset,
98110
#[cfg(gdb)]
99111
guest_debug_info,
100112
}
@@ -136,6 +148,29 @@ impl SandboxConfiguration {
136148
self.interrupt_retry_delay
137149
}
138150

151+
/// Get the signal offset from `SIGRTMIN` used to determine the signal number for interrupting the VCPU thread
152+
pub fn get_interrupt_vcpu_sigrtmin_offset(&self) -> u8 {
153+
self.interrupt_vcpu_sigrtmin_offset
154+
}
155+
156+
/// Sets the offset from `SIGRTMIN` to determine the real-time signal used for
157+
/// interrupting the VCPU thread.
158+
///
159+
/// The final signal number is computed as `SIGRTMIN + offset`, and it must fall within
160+
/// the valid range of real-time signals supported by the host system.
161+
///
162+
/// Returns Ok(()) if the offset is valid, or an error if it exceeds the maximum real-time signal number.
163+
pub fn set_interrupt_vcpu_sigrtmin_offset(&mut self, offset: u8) -> crate::Result<()> {
164+
if libc::SIGRTMIN() + offset as c_int > libc::SIGRTMAX() {
165+
return Err(crate::new_error!(
166+
"Invalid SIGRTMIN offset: {}. It exceeds the maximum real-time signal number.",
167+
offset
168+
));
169+
}
170+
self.interrupt_vcpu_sigrtmin_offset = offset;
171+
Ok(())
172+
}
173+
139174
/// Sets the configuration for the guest debug
140175
#[cfg(gdb)]
141176
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
@@ -195,6 +230,7 @@ impl Default for SandboxConfiguration {
195230
None,
196231
None,
197232
Self::DEFAULT_INTERRUPT_RETRY_DELAY,
233+
Self::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
198234
#[cfg(gdb)]
199235
None,
200236
)
@@ -218,6 +254,7 @@ mod tests {
218254
Some(STACK_SIZE_OVERRIDE),
219255
Some(HEAP_SIZE_OVERRIDE),
220256
SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
257+
SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
221258
#[cfg(gdb)]
222259
None,
223260
);
@@ -244,6 +281,7 @@ mod tests {
244281
None,
245282
None,
246283
SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
284+
SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
247285
#[cfg(gdb)]
248286
None,
249287
);

src/hyperlight_host/src/sandbox/uninitialized_evolve.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ where
8888
let dbg_mem_access_hdl = dbg_mem_access_handler_wrapper(hshm.clone());
8989

9090
#[cfg(target_os = "linux")]
91-
setup_signal_handlers()?;
91+
setup_signal_handlers(&u_sbox.config)?;
9292

9393
vm.initialise(
9494
peb_addr,

src/hyperlight_host/src/signal_handlers/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
use libc::c_int;
18+
19+
use crate::sandbox::SandboxConfiguration;
20+
1721
#[cfg(feature = "seccomp")]
1822
pub mod sigsys_signal_handler;
1923

20-
pub(crate) const INTERRUPT_VCPU_SIGRTMIN_OFFSET: i32 = 0;
21-
22-
pub(crate) fn setup_signal_handlers() -> crate::Result<()> {
24+
pub(crate) fn setup_signal_handlers(config: &SandboxConfiguration) -> crate::Result<()> {
2325
// This is unsafe because signal handlers only allow a very restrictive set of
2426
// functions (i.e., async-signal-safe functions) to be executed inside them.
2527
// Anything that performs memory allocations, locks, and others are non-async-signal-safe.
@@ -48,7 +50,7 @@ pub(crate) fn setup_signal_handlers() -> crate::Result<()> {
4850
}));
4951
}
5052
vmm_sys_util::signal::register_signal_handler(
51-
libc::SIGRTMIN() + INTERRUPT_VCPU_SIGRTMIN_OFFSET,
53+
libc::SIGRTMIN() + config.get_interrupt_vcpu_sigrtmin_offset() as c_int,
5254
vm_kill_signal,
5355
)?;
5456

src/hyperlight_host/tests/integration_test.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,47 @@ fn interrupt_moved_sandbox() {
283283
thread2.join().expect("Thread should finish");
284284
}
285285

286+
#[test]
287+
fn interrupt_custom_signal_no_and_retry_delay() {
288+
env_logger::builder().filter_level(LevelFilter::Info).init();
289+
let mut config = SandboxConfiguration::default();
290+
config.set_interrupt_vcpu_sigrtmin_offset(0).unwrap();
291+
config.set_interrupt_retry_delay(Duration::from_secs(1));
292+
293+
let mut sbox1: MultiUseSandbox = UninitializedSandbox::new(
294+
GuestBinary::FilePath(simple_guest_as_string().unwrap()),
295+
Some(config),
296+
)
297+
.unwrap()
298+
.evolve(Noop::default())
299+
.unwrap();
300+
301+
let interrupt_handle = sbox1.interrupt_handle();
302+
assert!(!interrupt_handle.dropped()); // not yet dropped
303+
let barrier = Arc::new(Barrier::new(2));
304+
let barrier2 = barrier.clone();
305+
306+
const NUM_ITERS: usize = 3;
307+
308+
let thread = thread::spawn(move || {
309+
for _ in 0..NUM_ITERS {
310+
barrier2.wait();
311+
// wait for the guest call to start
312+
thread::sleep(Duration::from_millis(1000));
313+
interrupt_handle.kill();
314+
}
315+
});
316+
317+
for _ in 0..NUM_ITERS {
318+
barrier.wait();
319+
let res = sbox1
320+
.call_guest_function_by_name::<i32>("Spin", ())
321+
.unwrap_err();
322+
assert!(matches!(res, HyperlightError::ExecutionCanceledByHost()));
323+
}
324+
thread.join().expect("Thread should finish");
325+
}
326+
286327
#[test]
287328
fn print_four_args_c_guest() {
288329
let path = c_simple_guest_as_string().unwrap();

0 commit comments

Comments
 (0)