From 716a2d38ac8d745a9973c6c24d0e491cd9be2bb4 Mon Sep 17 00:00:00 2001 From: Juliette Pretot Date: Fri, 22 Mar 2024 16:04:32 +0000 Subject: [PATCH] Implement seperate syscall for switching to an application --- enclave_apps/oak_orchestrator/src/main.rs | 5 +- oak_restricted_kernel/src/lib.rs | 30 ++++++++++-- oak_restricted_kernel/src/payload.rs | 20 ++++---- .../src/syscall/create_process.rs | 49 +++++++++++++++++++ oak_restricted_kernel/src/syscall/mod.rs | 15 ++++-- .../src/syscall/switch_process.rs | 30 ++---------- .../src/syscall.rs | 24 +++++++-- .../src/syscalls.rs | 13 ++++- oak_restricted_kernel_launcher/README.md | 2 +- 9 files changed, 136 insertions(+), 52 deletions(-) create mode 100644 oak_restricted_kernel/src/syscall/create_process.rs diff --git a/enclave_apps/oak_orchestrator/src/main.rs b/enclave_apps/oak_orchestrator/src/main.rs index c087dd44000..007347b101d 100644 --- a/enclave_apps/oak_orchestrator/src/main.rs +++ b/enclave_apps/oak_orchestrator/src/main.rs @@ -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) } diff --git a/oak_restricted_kernel/src/lib.rs b/oak_restricted_kernel/src/lib.rs index a564199b194..4b6ff83fe49 100644 --- a/oak_restricted_kernel/src/lib.rs +++ b/oak_restricted_kernel/src/lib.rs @@ -124,6 +124,30 @@ pub static VMA_ALLOCATOR: Spinlock> = }, ))); +static PROCCESSES: Processes = Processes { list: Spinlock::new(alloc::vec::Vec::new()) }; + +struct Processes { + list: Spinlock>, +} + +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(); @@ -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)] diff --git a/oak_restricted_kernel/src/payload.rs b/oak_restricted_kernel/src/payload.rs index a60ebb1daa9..d74efbe591c 100644 --- a/oak_restricted_kernel/src/payload.rs +++ b/oak_restricted_kernel/src/payload.rs @@ -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 @@ -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 { + pub unsafe fn from_application(application: &Application) -> Result { 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. @@ -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. diff --git a/oak_restricted_kernel/src/syscall/create_process.rs b/oak_restricted_kernel/src/syscall/create_process.rs new file mode 100644 index 00000000000..d4138efca4c --- /dev/null +++ b/oak_restricted_kernel/src/syscall/create_process.rs @@ -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 { + // Copy the ELF file into kernel space. + let copied_elf_binary: alloc::vec::Vec = 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") }, + ) +} diff --git a/oak_restricted_kernel/src/syscall/mod.rs b/oak_restricted_kernel/src/syscall/mod.rs index b15113c8af7..dcdb71f5477 100644 --- a/oak_restricted_kernel/src/syscall/mod.rs +++ b/oak_restricted_kernel/src/syscall/mod.rs @@ -22,6 +22,8 @@ pub mod mmap; mod process; mod stdio; +#[cfg(feature = "initrd")] +mod create_process; #[cfg(feature = "initrd")] mod switch_process; @@ -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, @@ -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, diff --git a/oak_restricted_kernel/src/syscall/switch_process.rs b/oak_restricted_kernel/src/syscall/switch_process.rs index cdd79730333..7f2725fae82 100644 --- a/oak_restricted_kernel/src/syscall/switch_process.rs +++ b/oak_restricted_kernel/src/syscall/switch_process.rs @@ -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) } diff --git a/oak_restricted_kernel_interface/src/syscall.rs b/oak_restricted_kernel_interface/src/syscall.rs index df21ac953eb..60390519718 100644 --- a/oak_restricted_kernel_interface/src/syscall.rs +++ b/oak_restricted_kernel_interface/src/syscall.rs @@ -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 { + 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!(); } diff --git a/oak_restricted_kernel_interface/src/syscalls.rs b/oak_restricted_kernel_interface/src/syscalls.rs index 504b24ddacb..1a1b6d507b3 100644 --- a/oak_restricted_kernel_interface/src/syscalls.rs +++ b/oak_restricted_kernel_interface/src/syscalls.rs @@ -96,13 +96,22 @@ pub enum Syscall { /// a value of 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 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 on failure; 0, otherwise. UnstableSwitchProcess = UNSTABLE_SYSCALL_SPACE + 1, } diff --git a/oak_restricted_kernel_launcher/README.md b/oak_restricted_kernel_launcher/README.md index 4b74b773dfa..5efa78ac1d1 100644 --- a/oak_restricted_kernel_launcher/README.md +++ b/oak_restricted_kernel_launcher/README.md @@ -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 \