Skip to content

Commit 9090ce2

Browse files
committed
Allow configuring the signal number of the signal used to interrupt a sandbox
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 15786fe commit 9090ce2

File tree

7 files changed

+96
-9
lines changed

7 files changed

+96
-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: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
use std::cmp::max;
1818
use std::time::Duration;
1919

20+
#[cfg(target_os = "linux")]
21+
use libc::c_int;
2022
use tracing::{instrument, Span};
2123

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

6878
impl SandboxConfiguration {
@@ -76,6 +86,8 @@ impl SandboxConfiguration {
7686
pub const MIN_OUTPUT_SIZE: usize = 0x2000;
7787
/// The default interrupt retry delay
7888
pub const DEFAULT_INTERRUPT_RETRY_DELAY: Duration = Duration::from_micros(500);
89+
/// The default signal offset from `SIGRTMIN` used to determine the signal number for interrupting
90+
pub const INTERRUPT_VCPU_SIGRTMIN_OFFSET: u8 = 0;
7991

8092
#[allow(clippy::too_many_arguments)]
8193
/// Create a new configuration for a sandbox with the given sizes.
@@ -86,6 +98,7 @@ impl SandboxConfiguration {
8698
stack_size_override: Option<u64>,
8799
heap_size_override: Option<u64>,
88100
interrupt_retry_delay: Duration,
101+
interrupt_vcpu_sigrtmin_offset: u8,
89102
#[cfg(gdb)] guest_debug_info: Option<DebugInfo>,
90103
) -> Self {
91104
Self {
@@ -94,7 +107,7 @@ impl SandboxConfiguration {
94107
stack_size_override: stack_size_override.unwrap_or(0),
95108
heap_size_override: heap_size_override.unwrap_or(0),
96109
interrupt_retry_delay,
97-
110+
interrupt_vcpu_sigrtmin_offset,
98111
#[cfg(gdb)]
99112
guest_debug_info,
100113
}
@@ -136,6 +149,31 @@ impl SandboxConfiguration {
136149
self.interrupt_retry_delay
137150
}
138151

152+
/// Get the signal offset from `SIGRTMIN` used to determine the signal number for interrupting the VCPU thread
153+
#[cfg(target_os = "linux")]
154+
pub fn get_interrupt_vcpu_sigrtmin_offset(&self) -> u8 {
155+
self.interrupt_vcpu_sigrtmin_offset
156+
}
157+
158+
/// Sets the offset from `SIGRTMIN` to determine the real-time signal used for
159+
/// interrupting the VCPU thread.
160+
///
161+
/// The final signal number is computed as `SIGRTMIN + offset`, and it must fall within
162+
/// the valid range of real-time signals supported by the host system.
163+
///
164+
/// Returns Ok(()) if the offset is valid, or an error if it exceeds the maximum real-time signal number.
165+
#[cfg(target_os = "linux")]
166+
pub fn set_interrupt_vcpu_sigrtmin_offset(&mut self, offset: u8) -> crate::Result<()> {
167+
if libc::SIGRTMIN() + offset as c_int > libc::SIGRTMAX() {
168+
return Err(crate::new_error!(
169+
"Invalid SIGRTMIN offset: {}. It exceeds the maximum real-time signal number.",
170+
offset
171+
));
172+
}
173+
self.interrupt_vcpu_sigrtmin_offset = offset;
174+
Ok(())
175+
}
176+
139177
/// Sets the configuration for the guest debug
140178
#[cfg(gdb)]
141179
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
@@ -195,6 +233,7 @@ impl Default for SandboxConfiguration {
195233
None,
196234
None,
197235
Self::DEFAULT_INTERRUPT_RETRY_DELAY,
236+
Self::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
198237
#[cfg(gdb)]
199238
None,
200239
)
@@ -218,6 +257,7 @@ mod tests {
218257
Some(STACK_SIZE_OVERRIDE),
219258
Some(HEAP_SIZE_OVERRIDE),
220259
SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
260+
SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
221261
#[cfg(gdb)]
222262
None,
223263
);
@@ -244,6 +284,7 @@ mod tests {
244284
None,
245285
None,
246286
SandboxConfiguration::DEFAULT_INTERRUPT_RETRY_DELAY,
287+
SandboxConfiguration::INTERRUPT_VCPU_SIGRTMIN_OFFSET,
247288
#[cfg(gdb)]
248289
None,
249290
);

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: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,48 @@ fn interrupt_moved_sandbox() {
283283
thread2.join().expect("Thread should finish");
284284
}
285285

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

0 commit comments

Comments
 (0)