Skip to content
This repository has been archived by the owner on Mar 24, 2022. It is now read-only.

KillSwitch terminates pending instance #429

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
41ed86d
lucet-runtime: KillSwitch can terminate pending instances
data-pup Feb 5, 2020
bdec494
lucet-runtime: invalid execution domain entering guest causes panic
data-pup Feb 20, 2020
a33e726
lucet-runtime: document `Domain` enum, and derive `Debug` for it
data-pup Feb 20, 2020
5dd5887
lucet-runtime: document killswitch methods to begin/end hostcall
data-pup Feb 21, 2020
35a049a
lucet-runtime: KillState does not need a terminable flag [..]
data-pup Feb 21, 2020
a33e18e
lucet-runtime: polish
data-pup Feb 21, 2020
6d13d63
lucet-runtime: pr review via @iximeow
data-pup Feb 24, 2020
2e27187
lucet-runtime: pr review ii
data-pup Feb 24, 2020
b566730
lucet-runtime: pr review iii
data-pup Feb 24, 2020
bf66595
lucet-runtime: pr review iv
data-pup Feb 25, 2020
ca9b3ad
lucet-runtime: `with_terminability` method [..]
data-pup Feb 25, 2020
437a63c
lucet-runtime: update termination documentation
data-pup Feb 25, 2020
7cde155
lucet-runtime: polish timeout tests
data-pup Feb 25, 2020
30cb276
lucet-runtime: fix comment phrasing
data-pup Feb 25, 2020
8a2d51d
lucet-runtime: update execution module docs
data-pup Feb 25, 2020
f92ac1d
pr review: documentation fixes
data-pup Mar 2, 2020
99ab3e0
`KillSwitch::terminable` is needed
data-pup Mar 13, 2020
957c12a
misc polish
data-pup Mar 13, 2020
1bc0707
Update lucet-runtime/lucet-runtime-internals/src/instance/execution.rs
cratelyn Mar 25, 2020
21bf244
avoid risk of UB in exit_guest_region using non-mut mut ptr
iximeow Mar 25, 2020
79c4d0f
reset kill states when returning from an instance run, except yielding
iximeow Mar 25, 2020
55430a3
Revert "reset kill states when returning from an instance run, except…
data-pup Mar 25, 2020
a6646d9
Revert "avoid risk of UB in exit_guest_region using non-mut mut ptr"
data-pup Mar 25, 2020
67c6677
fix `exit_guest_region` memory leak
data-pup Mar 25, 2020
0025a01
Update lucet-runtime/lucet-runtime-internals/src/instance.rs
cratelyn Mar 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions lucet-runtime/lucet-runtime-internals/src/context/context_asm.S
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ _lucet_context_backstop:
jz no_backstop_callback
#endif

// load `backstop_data`, arg 1
// load `callback_data`, arg 1
mov (10*8 + 8*16 + 8*2 + 16 + 8 + 8)(%rbp), %rdi
// call `backstop_callback`
call *%rsi
Expand Down Expand Up @@ -199,13 +199,23 @@ _lucet_context_set:
.globl _lucet_context_activate
#endif
.align 16
// lucet_context_activate is essentially a function with two arguments:
// in rdi, the address of this guest's "running" flag.
// in rsi, the address of the guest code to resume at.
// `lucet_context_activate` is essentially a function with three arguments:
// * rdi: the data for the entry callback.
// * rsi: the address of the entry callback.
// * rbx: the address of the guest code to execute.
//
// See `lucet_runtime_internals::context::lucet_context_activate` for more info.
//
// Note that `rbx` is used to store the address of the guest code because it is
// a callee-saved register in the System V calling convention. It is also a
// non-violatile register on Windows, which is a nice benefit.
lucet_context_activate:
_lucet_context_activate:
movb $1, (%rdi)
jmp *%rsi
// First, we call the entry callback whose address is stored in `rsi`,
// passing along the value of `rdi` as the first argument.
call *%rsi
// Now, jump to the guest code at the address in `rbx`.
jmp *%rbx
#ifdef __ELF__
.size lucet_context_activate,.-lucet_context_activate
#endif
Expand Down
39 changes: 30 additions & 9 deletions lucet-runtime/lucet-runtime-internals/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use thiserror::Error;
/// `u64`, this should be fine?
#[repr(C)]
pub(crate) struct GpRegs {
rbx: u64,
pub(crate) rbx: u64,
pub(crate) rsp: u64,
rbp: u64,
pub(crate) rdi: u64,
Expand Down Expand Up @@ -122,7 +122,7 @@ pub struct Context {
parent_ctx: *mut Context,
// TODO ACF 2019-10-23: make Instance into a generic parameter?
backstop_callback: *const unsafe extern "C" fn(*mut Instance),
backstop_data: *mut Instance,
callback_data: *mut Instance,
sigset: signal::SigSet,
}

Expand All @@ -136,10 +136,15 @@ impl Context {
retval_fp: unsafe { _mm_setzero_ps() },
parent_ctx: ptr::null_mut(),
backstop_callback: Context::default_backstop_callback as *const _,
backstop_data: ptr::null_mut(),
callback_data: ptr::null_mut(),
sigset: signal::SigSet::empty(),
}
}

/// Get a raw pointer to the instance's callback data.
pub(crate) fn callback_data_ptr(&self) -> *mut Instance {
self.callback_data
}
}

/// A wrapper around a `Context`, primarily meant for use in test code.
Expand Down Expand Up @@ -383,12 +388,12 @@ impl Context {
/// guest entrypoint returns.
///
/// After the entrypoint function returns, but before swapping back to the parent context,
/// `backstop_callback` will be run with the single argument `backstop_data`.
/// `backstop_callback` will be run with the single argument `callback_data`.
pub fn init_with_callback(
stack: &mut [u64],
child: &mut Context,
backstop_callback: unsafe extern "C" fn(*mut Instance),
backstop_data: *mut Instance,
callback_data: *mut Instance,
fptr: usize,
args: &[Val],
) -> Result<(), Error> {
Expand All @@ -398,7 +403,7 @@ impl Context {

if backstop_callback != Context::default_backstop_callback {
child.backstop_callback = backstop_callback as *const _;
child.backstop_data = backstop_data;
child.callback_data = callback_data;
}

let mut gp_args_ix = 0;
Expand Down Expand Up @@ -723,9 +728,25 @@ extern "C" {
/// Never returns because the current context is discarded.
fn lucet_context_set(to: *const Context) -> !;

/// Enables termination for the instance, after performing a context switch.
/// Runs an entry callback after performing a context switch. Implemented in assembly.
///
/// In practice, this is used with `enter_guest_region` so that the guest will appropriately
/// set itself to be terminable upon entry before continuing to any guest code.
///
/// `lucet_context_activate` is essentially a function with three arguments:
/// * rdi: the data for the entry callback.
/// * rsi: the address of the entry callback.
/// * rbx: the address of the guest code to execute.
///
/// We do not actually define `lucet_context_activate` as having these arguments because we
/// manually load these arguments, as well as a pointer to this function, into the context's
/// registers. See `Instance::with_activation_routine` for more information.
///
/// Note that `rbx` is used to store the address of the guest code because it is a callee-saved
/// register in the System V calling convention. It is also a non-violatile register on
/// Windows, which is a nice additional benefit.
cratelyn marked this conversation as resolved.
Show resolved Hide resolved
///
/// Takes the guest return address as an argument as a consequence of implementation details,
/// see `Instance::swap_and_return` for more.
/// For more information, see `Instance::swap_and_return`, `Instance::with_activation_routine`,
/// `enter_guest_region`, and `lucet_context_activate`'s assembly implementation.
pub(crate) fn lucet_context_activate();
}
64 changes: 40 additions & 24 deletions lucet-runtime/lucet-runtime-internals/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,15 @@ impl Instance {
/// modified by this call; it is the embedder's responsibility to clear or reset their state if
/// necessary.
///
/// This will also reinitialize the kill state, which means that any outstanding
/// [`KillSwitch`](struct.KillSwitch.html) objects will be unable to terminate this instance.
/// It is the embedder's responsibility to initialize new `KillSwitch`es after resetting an
/// instance.
///
/// # Safety
///
/// This function runs the guest code for the WebAssembly `start` section, and running any guest
/// code is potentially unsafe; see [`Instance::run()`](struct.Instance.html#method.run).
/// This function runs the guest code for the WebAssembly `start` section, and running any
/// guest code is potentially unsafe; see [`Instance::run()`](struct.Instance.html#method.run).
pub fn reset(&mut self) -> Result<(), Error> {
self.alloc.reset_heap(self.module.as_ref())?;
let globals = unsafe { self.alloc.globals_mut() };
Expand All @@ -554,9 +559,8 @@ impl Instance {
}

self.state = State::Ready;

self.kill_state = Arc::new(KillState::new());
self.run_start()?;

Ok(())
}

Expand Down Expand Up @@ -856,30 +860,42 @@ impl Instance {
&args_with_vmctx,
)?;

// Set up the guest to set itself as terminable, then continue to
// whatever guest code we want to run.
//
// `lucet_context_activate` takes two arguments:
// rsi: address of guest code to execute
// rdi: pointer to a bool that indicates the guest can be terminated
//
// The appropriate value for `rsi` is the top of the guest stack, which
// we would otherwise return to and start executing immediately. For
// `rdi`, we want to pass a pointer to the instance's `terminable` flag.
//
// once we've set up arguments, swap out the guest return address with
// `lucet_context_activate` so we start execution there.
self.install_activator();
self.swap_and_return()
}

/// Prepare the guest so that it will update its execution domain upon entry.
///
/// This mutates the context's registers so that an activation function that will be run after
/// performing a context switch. This function (`enter_guest_region`) will mark the guest as
/// terminable before continuing to whatever guest code we want to run.
///
/// `lucet_context_activate` takes three arguments in the following registers:
/// * rdi: the data for the entry callback.
/// * rsi: the address of the entry callback.
/// * rbx: the address of the guest code to execute.
///
/// The appropriate value for `rbx` is the top of the guest stack, which we would otherwise
/// return to and start executing immediately. For `rdi`, we want to pass our callback data
/// (a raw pointer to the instance). This will be passed as the first argument to the entry
/// function, which is responsible for updating the kill state's execution domain.
///
/// See `lucet_runtime_internals::context::lucet_context_activate`, and
/// `execution::enter_guest_region` for more info.
// TODO KTM 2020-03-13: This should be a method on `Context`.
fn install_activator(&mut self) {
unsafe {
// Get a raw pointer to the top of the guest stack.
let top_of_stack = self.ctx.gpr.rsp as *mut u64;
// move the guest code address to rsi
self.ctx.gpr.rsi = *top_of_stack;
// replace it with the activation thunk
// Move the guest code address to rbx, and then put the address of the activation thunk
// at the top of the stack, so that we will start execution at `enter_guest_region`.
self.ctx.gpr.rbx = *top_of_stack;
*top_of_stack = crate::context::lucet_context_activate as u64;
// and store a pointer to indicate we're active
self.ctx.gpr.rdi = self.kill_state.terminable_ptr() as u64;
// Pass a pointer to our guest-side entrypoint bootstrap code in `rsi`, and then put
// its first argument (a raw pointer to `self`) in `rdi`.
self.ctx.gpr.rsi = execution::enter_guest_region as u64;
self.ctx.gpr.rdi = self.ctx.callback_data_ptr() as u64;
}

self.swap_and_return()
}

/// The core routine for context switching into a guest, and extracting a result.
Expand Down
Loading