Skip to content

Commit

Permalink
Implement seperate syscall for switching to an application
Browse files Browse the repository at this point in the history
  • Loading branch information
jul-sh committed Mar 22, 2024
1 parent b88b074 commit 716a2d3
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 52 deletions.
5 changes: 3 additions & 2 deletions enclave_apps/oak_orchestrator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn start() -> ! {
.expect("failed to write dice data");
attested_app.dice_data.as_bytes_mut().zeroize();

log::info!("Exiting and launching application.");
syscall::unstable_switch_proccess(attested_app.elf_binary.as_slice())
let pid = syscall::unstable_create_proccess(attested_app.elf_binary.as_slice())
.expect("failed to create app process");
syscall::unstable_switch_proccess(pid)
}
30 changes: 26 additions & 4 deletions oak_restricted_kernel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,30 @@ pub static VMA_ALLOCATOR: Spinlock<VirtualAddressAllocator<Size2MiB>> =
},
)));

static PROCCESSES: Processes = Processes { list: Spinlock::new(alloc::vec::Vec::new()) };

struct Processes {
list: Spinlock<alloc::vec::Vec<Process>>,
}

impl Processes {
/// # Safety
///
/// Caller must ensure to no lock is currently being held.
unsafe fn add(&self, process: Process) -> usize {
self.list.force_unlock();
let mut processes = self.list.lock();
let pid: usize = processes.len();
processes.push(process);
log::debug!("Created process (pid: {})", pid);
pid
}
fn execute(&self, pid: usize) -> ! {
log::debug!("Executing process (pid: {})", pid);
self.list.lock().get_mut(pid).expect("PID not mapped to a process").execute()
}
}

/// Main entry point for the kernel, to be called from bootloader.
pub fn start_kernel(info: &BootParams) -> ! {
avx::enable_avx();
Expand Down Expand Up @@ -471,11 +495,9 @@ pub fn start_kernel(info: &BootParams) -> ! {

// Ensure new process is not dropped.
// Safety: The application is assumed to be a valid ELF file.
let process = Box::leak(Box::new(unsafe {
Process::from_application(&application).expect("failed to create process")
}));
let pid = unsafe { Process::from_application(&application).expect("failed to create process") };

process.execute()
PROCCESSES.execute(pid)
}

#[derive(EnumIter, EnumString)]
Expand Down
20 changes: 10 additions & 10 deletions oak_restricted_kernel/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ use goblin::{
use oak_restricted_kernel_interface::syscalls::{MmapFlags, MmapProtection};
use self_cell::self_cell;
use x86_64::{
structures::paging::{PageSize, Size2MiB},
structures::paging::{PageSize, PhysFrame, Size2MiB},
VirtAddr,
};

use crate::syscall::mmap::mmap;
use crate::{syscall::mmap::mmap, PROCCESSES};

// Set up the userspace stack at the end of the lower half of the virtual
// address space. Well... almost. It's one page lower than the very end, as
Expand Down Expand Up @@ -160,19 +160,21 @@ pub fn identify_pml4_frame(
}

pub struct Process {
pml4: x86_64::structures::paging::PageTable,
pml4_frame: PhysFrame,
entry: VirtAddr,
}

impl Process {
/// Creates a process from the application, without executing it.
/// Creates a process from the application, without executing it. Returns
/// the PID of the new process.
///
/// # Safety
///
/// The application must be built from a valid ELF file representing an Oak
/// Restricted Application.
pub unsafe fn from_application(application: &Application) -> Result<Self, anyhow::Error> {
pub unsafe fn from_application(application: &Application) -> Result<usize, anyhow::Error> {
let pml4 = crate::BASE_L4_PAGE_TABLE.get().context("base l4 table should be set")?.clone();
let pml4_frame: PhysFrame = identify_pml4_frame(&pml4)?;
// Load the process's page table, so the application can be loaded into its
// memory. Hold onto the previous PT, so we can revert to it once the
// application has been mapped into the process pt.
Expand All @@ -198,17 +200,15 @@ impl Process {
// Safety: the new page table maintains the same mappings for kernel space.
unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame) };
}

Ok(Self { pml4, entry })
let pid = PROCCESSES.add(Self { pml4_frame, entry });
Ok(pid)
}
/// Executes the process.
pub fn execute(&self) -> ! {
let pml4_frame = identify_pml4_frame(&self.pml4).expect("could not get pml4 frame");
// Safety: the new page table maintains the same mappings for kernel space.
unsafe { crate::PAGE_TABLES.lock().replace(pml4_frame) };
unsafe { crate::PAGE_TABLES.lock().replace(self.pml4_frame) };

let entry = self.entry;
log::info!("Running application");
// Enter Ring 3 and jump to user code.
// Safety: by now, if we're here, we've loaded a valid ELF file. It's up to the
// user to guarantee that the file made sense.
Expand Down
49 changes: 49 additions & 0 deletions oak_restricted_kernel/src/syscall/create_process.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright 2024 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use core::{
ffi::{c_size_t, c_ssize_t, c_void},
slice,
};

use oak_restricted_kernel_interface::Errno;

use crate::payload::Process;

pub fn syscall_unstable_create_proccess(buf: *mut c_void, count: c_size_t) -> c_ssize_t {
// We should validate that the pointer and count are valid, as these come from
// userspace and therefore are not to be trusted, but right now everything
// is in kernel space so there is nothing to check.
let elf_binary_buffer = unsafe { slice::from_raw_parts(buf as *mut u8, count) };
match unstable_create_proccess(elf_binary_buffer) {
Ok(pid) => pid.try_into().expect("pid so large, it could not be represented as isize"),
Err(err) => err as isize,
}
}

fn unstable_create_proccess(buf: &[u8]) -> Result<usize, Errno> {
// Copy the ELF file into kernel space.
let copied_elf_binary: alloc::vec::Vec<u8> = buf.to_vec();

let application = crate::payload::Application::new(copied_elf_binary.into_boxed_slice())
.inspect_err(|err| log::error!("failed to create application: {:?}", err))
.map_err(|_| Errno::EINVAL)?;

Ok(
// Safety: application is assumed to be a valid ELF file.
unsafe { Process::from_application(&application).expect("failed to create process") },
)
}
15 changes: 12 additions & 3 deletions oak_restricted_kernel/src/syscall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub mod mmap;
mod process;
mod stdio;

#[cfg(feature = "initrd")]
mod create_process;
#[cfg(feature = "initrd")]
mod switch_process;

Expand All @@ -44,7 +46,10 @@ use x86_64::{
};

#[cfg(feature = "initrd")]
use self::switch_process::syscall_unstable_switch_proccess;
use self::{
create_process::syscall_unstable_create_proccess,
switch_process::syscall_unstable_switch_proccess,
};
use self::{
fd::{syscall_fsync, syscall_read, syscall_write},
mmap::syscall_mmap,
Expand Down Expand Up @@ -131,9 +136,13 @@ extern "sysv64" fn syscall_handler(
}
Some(Syscall::Fsync) => syscall_fsync(arg1 as i32),
#[cfg(feature = "initrd")]
Some(Syscall::UnstableSwitchProcess) => {
syscall_unstable_switch_proccess(arg1 as *mut c_void, arg2)
Some(Syscall::UnstableCreateProcess) => {
syscall_unstable_create_proccess(arg1 as *mut c_void, arg2)
}
#[cfg(feature = "initrd")]
Some(Syscall::UnstableSwitchProcess) => syscall_unstable_switch_proccess(arg1),
#[cfg(not(feature = "initrd"))]
Some(Syscall::UnstableCreateProcess) => Errno::ENOSYS as isize,
#[cfg(not(feature = "initrd"))]
Some(Syscall::UnstableSwitchProcess) => Errno::ENOSYS as isize,
None => Errno::ENOSYS as isize,
Expand Down
30 changes: 4 additions & 26 deletions oak_restricted_kernel/src/syscall/switch_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,9 @@
// limitations under the License.
//

use alloc::boxed::Box;
use core::{
ffi::{c_size_t, c_void},
slice,
};
use core::ffi::c_size_t;

use crate::payload::Process;

pub fn syscall_unstable_switch_proccess(buf: *mut c_void, count: c_size_t) -> ! {
// We should validate that the pointer and count are valid, as these come from
// userspace and therefore are not to be trusted, but right now everything
// is in kernel space so there is nothing to check.
let elf_binary_buffer = unsafe { slice::from_raw_parts(buf as *mut u8, count) };

// Copy the ELF file into kernel space.
let copied_elf_binary = elf_binary_buffer.to_vec();

let application = crate::payload::Application::new(copied_elf_binary.into_boxed_slice())
.expect("failed to parse application");

// Ensure the new process is not dropped.
let process = Box::leak(Box::new(
// Safety: application is assumed to be a valid ELF file.
unsafe { Process::from_application(&application).expect("failed to create process") },
));

process.execute()
pub fn syscall_unstable_switch_proccess(pid: c_size_t) -> ! {
log::debug!("Switching to a different process (pid: {})", pid);
crate::PROCCESSES.execute(pid)
}
24 changes: 20 additions & 4 deletions oak_restricted_kernel_interface/src/syscall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,28 @@ pub fn exit(status: i32) -> ! {
}

#[no_mangle]
pub extern "C" fn sys_unstable_switch_proccess(buf: *const c_void, count: c_size_t) {
unsafe { syscall!(Syscall::UnstableSwitchProcess, buf, count) };
pub extern "C" fn sys_unstable_create_proccess(buf: *const c_void, count: c_size_t) -> c_ssize_t {
unsafe { syscall!(Syscall::UnstableCreateProcess, buf, count) }
}

pub fn unstable_switch_proccess(buf: &[u8]) -> ! {
sys_unstable_switch_proccess(buf.as_ptr() as *const c_void, buf.len());
pub fn unstable_create_proccess(buf: &[u8]) -> Result<usize, Errno> {
let ret = sys_unstable_create_proccess(buf.as_ptr() as *const c_void, buf.len());
if ret <= 0 {
Err(Errno::from_repr(ret).unwrap_or_else(|| {
panic!("unexpected error from unstable_create_proccess syscall: {}", ret)
}))
} else {
Ok(ret.try_into().expect("pid could not be represented as isize"))
}
}

#[no_mangle]
pub extern "C" fn sys_unstable_switch_proccess(pid: c_size_t) {
unsafe { syscall!(Syscall::UnstableSwitchProcess, pid) };
}

pub fn unstable_switch_proccess(pid: usize) -> ! {
sys_unstable_switch_proccess(pid);
unreachable!();
}

Expand Down
13 changes: 11 additions & 2 deletions oak_restricted_kernel_interface/src/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,22 @@ pub enum Syscall {
/// a value of <errno::Errno> on failure; 0, otherwise.
Fsync = 74,

/// Terminates the calling process and executes the supplied ELF binary
/// instead.
/// Create new process from ELF file, without starting it.
///
/// Arguments:
/// - arg0 (*mut c_void): pointer to the a buffer holding an ELF file
/// - arg1 (c_size_t): size of the buffer
/// Returns:
/// a value of <errno::Errno> on failure; Otherwise the PID of the new
/// process.
UnstableCreateProcess = UNSTABLE_SYSCALL_SPACE,

/// Switch the active execution execution to the process with the provided
/// PID.
///
/// Arguments:
/// - arg0 (c_size_t): PID of the process.
/// Returns:
/// a value of <errno::Errno> on failure; 0, otherwise.
UnstableSwitchProcess = UNSTABLE_SYSCALL_SPACE + 1,
}
Expand Down
2 changes: 1 addition & 1 deletion oak_restricted_kernel_launcher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ must be built.

```shell
# Stage0, the restricted kernel, and an enclave app may be built like so:
just stage0_bin oak_restricted_kernel_wrapper oak_orchestrator && \
just stage0_bin oak_restricted_kernel_wrapper oak_orchestrator oak_echo_raw_enclave_app && \

# After building dependencies, an enclave app may be run like so:
RUST_LOG=DEBUG \
Expand Down

0 comments on commit 716a2d3

Please sign in to comment.