[libos] Add PKU support
This commit is contained in:
parent
46ccbd4ee4
commit
338dda643b
@ -173,6 +173,10 @@ enclave {
|
|||||||
|
|
||||||
int occlum_ocall_mprotect([user_check] void* addr, size_t len, int prot);
|
int occlum_ocall_mprotect([user_check] void* addr, size_t len, int prot);
|
||||||
|
|
||||||
|
int occlum_ocall_pkey_alloc(unsigned int flags, unsigned int access_rights);
|
||||||
|
int occlum_ocall_pkey_mprotect([user_check] void* addr, size_t len, int prot, int pkey);
|
||||||
|
int occlum_ocall_pkey_free(int pkey);
|
||||||
|
|
||||||
int occlum_ocall_get_numa_topology(
|
int occlum_ocall_get_numa_topology(
|
||||||
[out, count=ncpus] uint32_t *numa_buf,
|
[out, count=ncpus] uint32_t *numa_buf,
|
||||||
size_t ncpus
|
size_t ncpus
|
||||||
|
@ -43,6 +43,7 @@ sgx1_exception_sim = [] # Simulate #PF and #GP exceptions on SGX 1
|
|||||||
dcap = [] # DCAP support. The compilation relies on DCAP package.
|
dcap = [] # DCAP support. The compilation relies on DCAP package.
|
||||||
cov = ["sgx_cov"] # Enable coverage colletcion.
|
cov = ["sgx_cov"] # Enable coverage colletcion.
|
||||||
hyper_mode = [] # For running in hyper mode.
|
hyper_mode = [] # For running in hyper mode.
|
||||||
|
pku = [] # PKU Support
|
||||||
|
|
||||||
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
[target.'cfg(not(target_env = "sgx"))'.dependencies]
|
||||||
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
sgx_types = { path = "../../deps/rust-sgx-sdk/sgx_types" }
|
||||||
|
@ -45,9 +45,12 @@ LIBOS_LOG ?= error
|
|||||||
|
|
||||||
LIBOS_SONAME := libocclum-libos.so.$(MAJOR_VER_NUM)
|
LIBOS_SONAME := libocclum-libos.so.$(MAJOR_VER_NUM)
|
||||||
|
|
||||||
|
LIBOS_FEATURES :=
|
||||||
|
|
||||||
ifeq ($(SGX_MODE), HW)
|
ifeq ($(SGX_MODE), HW)
|
||||||
LIBOS_CORE_LIB_NAME := occlum-libos-core
|
LIBOS_CORE_LIB_NAME := occlum-libos-core
|
||||||
LIBOS_SO_REAL := $(BUILD_DIR)/lib/libocclum-libos.so.$(VERSION_NUM)
|
LIBOS_SO_REAL := $(BUILD_DIR)/lib/libocclum-libos.so.$(VERSION_NUM)
|
||||||
|
LIBOS_FEATURES += pku
|
||||||
else ifeq ($(SGX_MODE), HYPER)
|
else ifeq ($(SGX_MODE), HYPER)
|
||||||
LIBOS_CORE_LIB_NAME := occlum-libos-core_hyper
|
LIBOS_CORE_LIB_NAME := occlum-libos-core_hyper
|
||||||
LIBOS_SO_REAL := $(BUILD_DIR)/lib/libocclum-libos_hyper.so.$(VERSION_NUM)
|
LIBOS_SO_REAL := $(BUILD_DIR)/lib/libocclum-libos_hyper.so.$(VERSION_NUM)
|
||||||
@ -122,8 +125,6 @@ else
|
|||||||
RUSTC_WRAPPER :=
|
RUSTC_WRAPPER :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBOS_FEATURES :=
|
|
||||||
|
|
||||||
ifndef OCCLUM_DISABLE_DCAP
|
ifndef OCCLUM_DISABLE_DCAP
|
||||||
LIBOS_FEATURES += dcap
|
LIBOS_FEATURES += dcap
|
||||||
endif
|
endif
|
||||||
|
@ -37,6 +37,9 @@ void do_exit_task(void);
|
|||||||
|
|
||||||
#else /* __ASSEMBLY__ */
|
#else /* __ASSEMBLY__ */
|
||||||
|
|
||||||
|
#define PKRU_LIBOS (0x0)
|
||||||
|
#define PKRU_USER (0x55555551)
|
||||||
|
|
||||||
/* See /<path-to-linux-sgx>/common/inc/internal/thread_data.h */
|
/* See /<path-to-linux-sgx>/common/inc/internal/thread_data.h */
|
||||||
#define TD_STACKGUARD_OFFSET (8 * 5)
|
#define TD_STACKGUARD_OFFSET (8 * 5)
|
||||||
/* Override the field for stack guard */
|
/* Override the field for stack guard */
|
||||||
|
@ -70,11 +70,19 @@ pub extern "C" fn occlum_ecall_init(
|
|||||||
// Init the log infrastructure first so that log messages will be printed afterwards
|
// Init the log infrastructure first so that log messages will be printed afterwards
|
||||||
util::log::init(log_level);
|
util::log::init(log_level);
|
||||||
|
|
||||||
// Init MPX for SFI if MPX is available
|
|
||||||
let report = rsgx_self_report();
|
let report = rsgx_self_report();
|
||||||
|
// Init MPX for SFI if MPX is available
|
||||||
if (report.body.attributes.xfrm & SGX_XFRM_MPX != 0) {
|
if (report.body.attributes.xfrm & SGX_XFRM_MPX != 0) {
|
||||||
util::mpx_util::mpx_enable();
|
util::mpx_util::mpx_enable();
|
||||||
}
|
}
|
||||||
|
// Init PKU for isolating LibOS form user-apps if PKU is available
|
||||||
|
// Occlum only turns on `pku` feature in HW mode
|
||||||
|
#[cfg(feature = "pku")]
|
||||||
|
{
|
||||||
|
if (report.body.attributes.xfrm & SGX_XFRM_PKRU != 0) {
|
||||||
|
crate::util::pku_util::try_set_pku_enabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Register exception handlers (support cpuid & rdtsc for now)
|
// Register exception handlers (support cpuid & rdtsc for now)
|
||||||
register_exception_handlers();
|
register_exception_handlers();
|
||||||
|
@ -14,6 +14,7 @@ use crate::fs::{
|
|||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::process::pgrp::{get_spawn_attribute_pgrp, update_pgrp_for_new_process};
|
use crate::process::pgrp::{get_spawn_attribute_pgrp, update_pgrp_for_new_process};
|
||||||
|
use crate::util::pku_util;
|
||||||
use crate::vm::ProcessVM;
|
use crate::vm::ProcessVM;
|
||||||
|
|
||||||
mod aux_vec;
|
mod aux_vec;
|
||||||
@ -447,7 +448,11 @@ fn init_auxvec(process_vm: &ProcessVM, exec_elf_file: &ElfFile) -> Result<AuxVec
|
|||||||
let ldso_elf_base = process_vm.get_elf_ranges()[1].start() as u64;
|
let ldso_elf_base = process_vm.get_elf_ranges()[1].start() as u64;
|
||||||
auxvec.set(AuxKey::AT_BASE, ldso_elf_base)?;
|
auxvec.set(AuxKey::AT_BASE, ldso_elf_base)?;
|
||||||
|
|
||||||
let syscall_addr = __occlum_syscall_linux_abi as *const () as u64;
|
let syscall_addr = if pku_util::check_pku_enabled() {
|
||||||
|
__occlum_syscall_linux_pku_abi
|
||||||
|
} else {
|
||||||
|
__occlum_syscall_linux_abi
|
||||||
|
} as *const () as u64;
|
||||||
auxvec.set(AuxKey::AT_OCCLUM_ENTRY, syscall_addr)?;
|
auxvec.set(AuxKey::AT_OCCLUM_ENTRY, syscall_addr)?;
|
||||||
// TODO: init AT_EXECFN
|
// TODO: init AT_EXECFN
|
||||||
// auxvec.set_val(AuxKey::AT_EXECFN, "program_name")?;
|
// auxvec.set_val(AuxKey::AT_EXECFN, "program_name")?;
|
||||||
@ -457,5 +462,6 @@ fn init_auxvec(process_vm: &ProcessVM, exec_elf_file: &ElfFile) -> Result<AuxVec
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn __occlum_syscall_linux_abi() -> i64;
|
fn __occlum_syscall_linux_abi() -> i64;
|
||||||
|
fn __occlum_syscall_linux_pku_abi() -> i64;
|
||||||
fn occlum_gdb_hook_load_elf(elf_base: u64, elf_path: *const u8, elf_path_len: u64);
|
fn occlum_gdb_hook_load_elf(elf_base: u64, elf_path: *const u8, elf_path_len: u64);
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,20 @@ __exec_task:
|
|||||||
|
|
||||||
// Use user stack
|
// Use user stack
|
||||||
movq TASK_USER_RSP(%rdi), %rsp
|
movq TASK_USER_RSP(%rdi), %rsp
|
||||||
// Run user code
|
// Get user code address
|
||||||
movq TASK_USER_ENTRY_ADDR(%rdi), %r11
|
movq TASK_USER_ENTRY_ADDR(%rdi), %r11
|
||||||
|
|
||||||
|
// Whether to switch PKRU value
|
||||||
|
mov pku_enabled(%rip), %r10
|
||||||
|
cmp $1, %r10
|
||||||
|
je update_pkru_in_exec_task
|
||||||
|
|
||||||
|
// Run user code
|
||||||
|
jmp *%r11
|
||||||
|
|
||||||
|
update_pkru_in_exec_task:
|
||||||
|
xor %ecx, %ecx
|
||||||
|
xor %edx, %edx
|
||||||
|
mov $PKRU_USER, %eax
|
||||||
|
wrpkru
|
||||||
jmp *%r11
|
jmp *%r11
|
||||||
|
@ -3,6 +3,22 @@
|
|||||||
|
|
||||||
|
|
||||||
.file "syscall_entry_x86-64.S"
|
.file "syscall_entry_x86-64.S"
|
||||||
|
.global __occlum_syscall_linux_pku_abi
|
||||||
|
.type __occlum_syscall_linux_pku_abi, @function
|
||||||
|
__occlum_syscall_linux_pku_abi:
|
||||||
|
pushq %rcx
|
||||||
|
pushq %rdx
|
||||||
|
pushq %rax
|
||||||
|
|
||||||
|
xor %ecx, %ecx
|
||||||
|
xor %edx, %edx
|
||||||
|
mov $PKRU_LIBOS, %eax
|
||||||
|
wrpkru
|
||||||
|
|
||||||
|
popq %rax
|
||||||
|
popq %rdx
|
||||||
|
popq %rcx
|
||||||
|
|
||||||
.global __occlum_syscall_linux_abi
|
.global __occlum_syscall_linux_abi
|
||||||
.type __occlum_syscall_linux_abi, @function
|
.type __occlum_syscall_linux_abi, @function
|
||||||
__occlum_syscall_linux_abi:
|
__occlum_syscall_linux_abi:
|
||||||
@ -128,7 +144,42 @@ __occlum_sysret:
|
|||||||
pop %rax
|
pop %rax
|
||||||
pop %rcx
|
pop %rcx
|
||||||
pop %rsp
|
pop %rsp
|
||||||
|
|
||||||
|
// Store RFLAGS since `cmp` operation may overwrite it
|
||||||
|
pushfq
|
||||||
|
push %rax
|
||||||
|
|
||||||
|
mov pku_enabled(%rip), %rax
|
||||||
|
cmp $1, %rax
|
||||||
|
je update_pkru_in_sysret
|
||||||
|
|
||||||
|
pop %rax
|
||||||
|
popfq
|
||||||
|
|
||||||
jmp *%gs:(TD_SYSCALL_RET_ADDR_OFFSET)
|
jmp *%gs:(TD_SYSCALL_RET_ADDR_OFFSET)
|
||||||
|
// This should never happen
|
||||||
|
ud2
|
||||||
|
|
||||||
|
update_pkru_in_sysret:
|
||||||
|
pop %rax
|
||||||
|
popfq
|
||||||
|
|
||||||
|
sub $0x20, %rsp
|
||||||
|
mov %rax, (%rsp)
|
||||||
|
mov %rdx, 0x8(%rsp)
|
||||||
|
mov %rcx, 0x10(%rsp)
|
||||||
|
mov %gs:(TD_SYSCALL_RET_ADDR_OFFSET), %rcx
|
||||||
|
mov %rcx, 0x18(%rsp)
|
||||||
|
|
||||||
|
xor %ecx, %ecx
|
||||||
|
xor %edx, %edx
|
||||||
|
mov $PKRU_USER, %eax
|
||||||
|
wrpkru
|
||||||
|
|
||||||
|
pop %rax
|
||||||
|
pop %rdx
|
||||||
|
pop %rcx
|
||||||
|
ret
|
||||||
|
|
||||||
.global __occlum_syscall_c_abi
|
.global __occlum_syscall_c_abi
|
||||||
.type __occlum_syscall_c_abi, @function
|
.type __occlum_syscall_c_abi, @function
|
||||||
|
@ -6,5 +6,6 @@ pub mod hosts_parser_util;
|
|||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod mem_util;
|
pub mod mem_util;
|
||||||
pub mod mpx_util;
|
pub mod mpx_util;
|
||||||
|
pub mod pku_util;
|
||||||
pub mod sgx;
|
pub mod sgx;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
|
101
src/libos/src/util/pku_util.rs
Normal file
101
src/libos/src/util/pku_util.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
/// Status variable accessed by assembly code
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut pku_enabled: u64 = 0;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref PKU_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PKEY_LIBOS: i32 = 0;
|
||||||
|
const PKEY_USER: i32 = 1;
|
||||||
|
|
||||||
|
/// Try enable PKU features in Occlum.
|
||||||
|
pub fn try_set_pku_enabled() {
|
||||||
|
// Alloc pkey
|
||||||
|
let mut pkey = -1;
|
||||||
|
let sgx_status = unsafe { occlum_ocall_pkey_alloc(&mut pkey, 0, 0) };
|
||||||
|
assert!(sgx_status == sgx_status_t::SGX_SUCCESS && pkey == PKEY_USER);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
pku_enabled = 1;
|
||||||
|
}
|
||||||
|
assert!(PKU_ENABLED.load(Ordering::Relaxed) == false);
|
||||||
|
PKU_ENABLED.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_pku_enabled() -> bool {
|
||||||
|
PKU_ENABLED.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pkey_mprotect_userspace_mem(user_mem_base: usize, user_mem_len: usize, perm: i32) {
|
||||||
|
if !self::check_pku_enabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut retval = -1;
|
||||||
|
debug!(
|
||||||
|
"associate memory region: 0x{:x} -> 0x{:x}, size: 0x{:x} with pkey for userspace: {:?}",
|
||||||
|
user_mem_base,
|
||||||
|
user_mem_base + user_mem_len,
|
||||||
|
user_mem_len,
|
||||||
|
PKEY_USER
|
||||||
|
);
|
||||||
|
let sgx_status = unsafe {
|
||||||
|
occlum_ocall_pkey_mprotect(
|
||||||
|
&mut retval,
|
||||||
|
user_mem_base as *const c_void,
|
||||||
|
user_mem_len,
|
||||||
|
perm,
|
||||||
|
PKEY_USER,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(sgx_status == sgx_status_t::SGX_SUCCESS && retval == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_pku_when_libos_exit(user_mem_base: usize, user_mem_len: usize, perm: i32) {
|
||||||
|
if !self::check_pku_enabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut retval = -1;
|
||||||
|
debug!(
|
||||||
|
"re-associate memory region 0x{:x} -> 0x{:x}, size: 0x{:x} with pkey for libos: {:?}",
|
||||||
|
user_mem_base,
|
||||||
|
user_mem_base + user_mem_len,
|
||||||
|
user_mem_len,
|
||||||
|
PKEY_LIBOS
|
||||||
|
);
|
||||||
|
let sgx_status = unsafe {
|
||||||
|
occlum_ocall_pkey_mprotect(
|
||||||
|
&mut retval,
|
||||||
|
user_mem_base as *const c_void,
|
||||||
|
user_mem_len,
|
||||||
|
perm,
|
||||||
|
PKEY_LIBOS,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(sgx_status == sgx_status_t::SGX_SUCCESS && retval == 0);
|
||||||
|
debug!("free pkey: {:?}", PKEY_USER);
|
||||||
|
let sgx_status = unsafe { occlum_ocall_pkey_free(&mut retval, PKEY_USER) };
|
||||||
|
assert!(sgx_status == sgx_status_t::SGX_SUCCESS && retval == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
pub fn occlum_ocall_pkey_alloc(
|
||||||
|
retval: *mut i32,
|
||||||
|
flags: u32,
|
||||||
|
access_rights: u32,
|
||||||
|
) -> sgx_status_t;
|
||||||
|
|
||||||
|
pub fn occlum_ocall_pkey_mprotect(
|
||||||
|
retval: *mut i32,
|
||||||
|
addr: *const c_void,
|
||||||
|
len: usize,
|
||||||
|
prot: i32,
|
||||||
|
pkey: i32,
|
||||||
|
) -> sgx_status_t;
|
||||||
|
|
||||||
|
pub fn occlum_ocall_pkey_free(retval: *mut i32, pkey: i32) -> sgx_status_t;
|
||||||
|
}
|
@ -1,10 +1,14 @@
|
|||||||
use super::ipc::SHM_MANAGER;
|
use super::ipc::SHM_MANAGER;
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ctor::dtor;
|
use crate::ctor::dtor;
|
||||||
|
use crate::util::pku_util;
|
||||||
use config::LIBOS_CONFIG;
|
use config::LIBOS_CONFIG;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use vm_manager::VMManager;
|
use vm_manager::VMManager;
|
||||||
|
|
||||||
|
const RSRV_MEM_PERM: MemPerm =
|
||||||
|
MemPerm::from_bits_truncate(MemPerm::READ.bits() | MemPerm::WRITE.bits());
|
||||||
|
|
||||||
/// The virtual memory manager for the entire user space
|
/// The virtual memory manager for the entire user space
|
||||||
pub struct UserSpaceVMManager(VMManager);
|
pub struct UserSpaceVMManager(VMManager);
|
||||||
|
|
||||||
@ -16,13 +20,13 @@ impl UserSpaceVMManager {
|
|||||||
// a lot of time. When EDMM is supported, there is no need to commit all the pages at the initialization stage. A function
|
// a lot of time. When EDMM is supported, there is no need to commit all the pages at the initialization stage. A function
|
||||||
// which reserves memory but not commit pages should be provided then.
|
// which reserves memory but not commit pages should be provided then.
|
||||||
let ptr = sgx_alloc_rsrv_mem(rsrv_mem_size);
|
let ptr = sgx_alloc_rsrv_mem(rsrv_mem_size);
|
||||||
let perm = MemPerm::READ | MemPerm::WRITE;
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return_errno!(ENOMEM, "run out of reserved memory");
|
return_errno!(ENOMEM, "run out of reserved memory");
|
||||||
}
|
}
|
||||||
// Change the page permission to RW (default)
|
// Change the page permission to RW (default)
|
||||||
assert!(
|
assert!(
|
||||||
sgx_tprotect_rsrv_mem(ptr, rsrv_mem_size, perm.bits()) == sgx_status_t::SGX_SUCCESS
|
sgx_tprotect_rsrv_mem(ptr, rsrv_mem_size, RSRV_MEM_PERM.bits())
|
||||||
|
== sgx_status_t::SGX_SUCCESS
|
||||||
);
|
);
|
||||||
|
|
||||||
let addr = ptr as usize;
|
let addr = ptr as usize;
|
||||||
@ -30,6 +34,7 @@ impl UserSpaceVMManager {
|
|||||||
"allocated rsrv addr is 0x{:x}, len is 0x{:x}",
|
"allocated rsrv addr is 0x{:x}, len is 0x{:x}",
|
||||||
addr, rsrv_mem_size
|
addr, rsrv_mem_size
|
||||||
);
|
);
|
||||||
|
pku_util::pkey_mprotect_userspace_mem(addr, rsrv_mem_size, RSRV_MEM_PERM.bits());
|
||||||
VMRange::new(addr, addr + rsrv_mem_size)?
|
VMRange::new(addr, addr + rsrv_mem_size)?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,10 +55,11 @@ fn free_user_space() {
|
|||||||
SHM_MANAGER.clean_when_libos_exit();
|
SHM_MANAGER.clean_when_libos_exit();
|
||||||
let range = USER_SPACE_VM_MANAGER.range();
|
let range = USER_SPACE_VM_MANAGER.range();
|
||||||
assert!(USER_SPACE_VM_MANAGER.verified_clean_when_exit());
|
assert!(USER_SPACE_VM_MANAGER.verified_clean_when_exit());
|
||||||
let addr = range.start() as *const c_void;
|
let addr = range.start();
|
||||||
let size = range.size();
|
let size = range.size();
|
||||||
info!("free user space VM: {:?}", range);
|
info!("free user space VM: {:?}", range);
|
||||||
assert!(unsafe { sgx_free_rsrv_mem(addr, size) == 0 });
|
pku_util::clear_pku_when_libos_exit(addr, size, RSRV_MEM_PERM.bits());
|
||||||
|
assert!(unsafe { sgx_free_rsrv_mem(addr as *const c_void, size) == 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for UserSpaceVMManager {
|
impl Deref for UserSpaceVMManager {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include "ocalls.h"
|
#include "ocalls.h"
|
||||||
@ -30,3 +31,15 @@ void occlum_ocall_free(void *ptr) {
|
|||||||
int occlum_ocall_mprotect(void *addr, size_t len, int prot) {
|
int occlum_ocall_mprotect(void *addr, size_t len, int prot) {
|
||||||
return mprotect(addr, len, prot);
|
return mprotect(addr, len, prot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int occlum_ocall_pkey_alloc(unsigned int flags, unsigned int access_rights) {
|
||||||
|
return pkey_alloc(flags, access_rights);
|
||||||
|
}
|
||||||
|
|
||||||
|
int occlum_ocall_pkey_mprotect(void *addr, size_t len, int prot, int pkey) {
|
||||||
|
return pkey_mprotect(addr, len, prot, pkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
int occlum_ocall_pkey_free(int pkey) {
|
||||||
|
return pkey_free(pkey);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user