| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 super::*; |
| use alloc::boxed::Box; |
| use alloc::vec::Vec; |
| use core::mem::{self, ManuallyDrop}; |
| use core::ptr; |
| use core::sync::atomic::{AtomicPtr, Ordering}; |
| use sgx_ffi::c_str::{CStr, CString}; |
| use sgx_ffi::memchr; |
| use sgx_sync::StaticRwLock; |
| use sgx_trts::trts::{is_within_enclave, EnclaveRange}; |
| use sgx_types::error::OsResult; |
| |
| pub unsafe fn getuid() -> OCallResult<uid_t> { |
| let mut result: uid_t = 0; |
| |
| let status = u_getuid_ocall(&mut result as *mut uid_t); |
| ensure!(status.is_success(), esgx!(status)); |
| Ok(result) |
| } |
| |
| pub unsafe fn getgid() -> OCallResult<gid_t> { |
| let mut result: gid_t = 0; |
| |
| let status = u_getgid_ocall(&mut result as *mut gid_t); |
| ensure!(status.is_success(), esgx!(status)); |
| Ok(result) |
| } |
| |
| pub unsafe fn getcwd() -> OCallResult<CString> { |
| let mut result: c_int = 0; |
| let mut error: c_int = 0; |
| let mut buf = Vec::with_capacity(1024); |
| |
| loop { |
| let bufsz = buf.capacity(); |
| let status = u_getcwd_ocall( |
| &mut result as *mut c_int, |
| &mut error as *mut c_int, |
| buf.as_mut_ptr() as *mut c_char, |
| bufsz, |
| ); |
| |
| ensure!(status.is_success(), esgx!(status)); |
| |
| if result == 0 { |
| buf.set_len(bufsz); |
| let value = shrink_to_fit_cstring(buf)?; |
| return Ok(value); |
| } else if error != ERANGE { |
| bail!(eos!(error)); |
| } |
| |
| // Trigger the internal buffer resizing logic of `Vec` by requiring |
| // more space than the current capacity. |
| buf.set_len(bufsz); |
| buf.reserve(1); |
| } |
| } |
| |
| pub unsafe fn chdir(dir: &CStr) -> OCallResult<()> { |
| let mut result: c_int = 0; |
| let mut error: c_int = 0; |
| |
| let status = u_chdir_ocall( |
| &mut result as *mut c_int, |
| &mut error as *mut c_int, |
| dir.as_ptr(), |
| ); |
| |
| ensure!(status.is_success(), esgx!(status)); |
| ensure!(result == 0, eos!(error)); |
| Ok(()) |
| } |
| |
| pub unsafe fn env() -> OCallResult<Vec<CString>> { |
| let _guard = ENV_LOCK.read(); |
| |
| let env_ptr = ENVIRON.load(Ordering::SeqCst); |
| ensure!( |
| !env_ptr.is_null(), |
| ecust!("Environment variables are not initialized") |
| ); |
| |
| let os_env = ManuallyDrop::new(Box::from_raw(env_ptr)); |
| Ok(os_env.env()) |
| } |
| |
| pub unsafe fn getenv(name: &CStr) -> OCallResult<Option<CString>> { |
| check_enclave_buffer(name.to_bytes_with_nul()).map_err(|_| eos!(EINVAL))?; |
| |
| let _guard = ENV_LOCK.read(); |
| |
| let env_ptr = ENVIRON.load(Ordering::SeqCst); |
| ensure!( |
| !env_ptr.is_null(), |
| ecust!("Environment variables are not initialized") |
| ); |
| |
| let os_env = ManuallyDrop::new(Box::from_raw(env_ptr)); |
| os_env.get(name).map_err(|e| eos!(e)) |
| } |
| |
| pub unsafe fn getenv_ref(name: &CStr) -> OCallResult<Option<&'static CStr>> { |
| check_enclave_buffer(name.to_bytes_with_nul()).map_err(|_| eos!(EINVAL))?; |
| |
| let _guard = ENV_LOCK.read(); |
| |
| let env_ptr = ENVIRON.load(Ordering::SeqCst); |
| ensure!( |
| !env_ptr.is_null(), |
| ecust!("Environment variables are not initialized") |
| ); |
| |
| let os_env = ManuallyDrop::new(Box::from_raw(env_ptr)); |
| os_env |
| .get_ptr(name) |
| .map(|v| v.map(|ptr| CStr::from_ptr(ptr))) |
| .map_err(|e| eos!(e)) |
| } |
| |
| pub unsafe fn setenv(name: &CStr, value: &CStr, overwrite: i32) -> OCallResult<()> { |
| check_enclave_buffer(name.to_bytes_with_nul()).map_err(|_| eos!(EINVAL))?; |
| check_enclave_buffer(value.to_bytes_with_nul()).map_err(|_| eos!(EINVAL))?; |
| |
| let _guard = ENV_LOCK.write(); |
| |
| let env_ptr = ENVIRON.load(Ordering::SeqCst); |
| ensure!( |
| !env_ptr.is_null(), |
| ecust!("Environment variables are not initialized") |
| ); |
| |
| let mut os_env = ManuallyDrop::new(Box::from_raw(env_ptr)); |
| |
| let overwrite = overwrite > 0; |
| os_env.set(name, value, overwrite).map_err(|e| eos!(e)) |
| } |
| |
| pub unsafe fn unsetenv(name: &CStr) -> OCallResult<()> { |
| check_enclave_buffer(name.to_bytes_with_nul()).map_err(|_| eos!(EINVAL))?; |
| |
| let _guard = ENV_LOCK.write(); |
| |
| let env_ptr = ENVIRON.load(Ordering::SeqCst); |
| ensure!( |
| !env_ptr.is_null(), |
| ecust!("Environment variables are not initialized") |
| ); |
| |
| let mut os_env = ManuallyDrop::new(Box::from_raw(env_ptr)); |
| os_env.remove(name).map_err(|e| eos!(e)) |
| } |
| |
| pub unsafe fn initenv(env: Option<Vec<CString>>) -> OCallResult<()> { |
| if !ENVIRON.load(Ordering::SeqCst).is_null() { |
| return Ok(()); |
| } |
| |
| let env = match env { |
| Some(env) => env, |
| None => { |
| #[cfg(feature = "init_env")] |
| { |
| env_ocall()? |
| } |
| #[cfg(not(feature = "init_env"))] |
| { |
| bail!(eos!(EINVAL)); |
| } |
| } |
| }; |
| |
| let _guard = ENV_LOCK.write(); |
| |
| if ENVIRON.load(Ordering::SeqCst).is_null() { |
| let os_env = Box::new(OsEnviron::new(env, &mut environ).map_err(|e| eos!(e))?); |
| ENVIRON.store(Box::into_raw(os_env), Ordering::SeqCst); |
| } |
| |
| return Ok(()); |
| |
| #[allow(dead_code)] |
| unsafe fn env_ocall() -> OCallResult<Vec<CString>> { |
| let mut result: ssize_t = 0; |
| let mut error: c_int = 0; |
| let mut buf: Vec<u8> = Vec::with_capacity(4096); |
| |
| loop { |
| let bufsz = buf.capacity(); |
| let status = u_env_ocall( |
| &mut result as *mut ssize_t, |
| &mut error as *mut c_int, |
| buf.as_mut_ptr() as *mut c_uchar, |
| bufsz, |
| ); |
| |
| ensure!(status.is_success(), esgx!(status)); |
| |
| if result >= 0 { |
| let buf_read = result as usize; |
| ensure!(buf_read <= bufsz, ecust!("Malformed return value.")); |
| buf.set_len(buf_read); |
| buf.shrink_to_fit(); |
| break; |
| } else if error != ERANGE { |
| bail!(eos!(error)); |
| } |
| |
| // Trigger the internal buffer resizing logic of `Vec` by requiring |
| // more space than the current capacity. |
| buf.set_len(bufsz); |
| buf.reserve(1); |
| } |
| |
| let env = buf |
| .split(|&c| c == 0) |
| .filter_map(|var| { |
| if !var.is_empty() { |
| CString::new(var).ok() |
| } else { |
| None |
| } |
| }) |
| .collect(); |
| Ok(env) |
| } |
| } |
| |
| pub unsafe fn args() -> OCallResult<Vec<CString>> { |
| let _guard = ARG_LOCK.read(); |
| |
| let args_ptr = ARGS.load(Ordering::SeqCst); |
| ensure!( |
| !args_ptr.is_null(), |
| ecust!("Command line arguments are not initialized") |
| ); |
| |
| let os_args = ManuallyDrop::new(Box::from_raw(args_ptr)); |
| Ok(os_args.args()) |
| } |
| |
| pub unsafe fn argc() -> OCallResult<usize> { |
| let _guard = ARG_LOCK.read(); |
| |
| let args_ptr = ARGS.load(Ordering::SeqCst); |
| ensure!( |
| !args_ptr.is_null(), |
| ecust!("Command line arguments are not initialized") |
| ); |
| |
| let os_args = ManuallyDrop::new(Box::from_raw(args_ptr)); |
| Ok(os_args.argc()) |
| } |
| |
| pub unsafe fn argv() -> OCallResult<*const *const c_char> { |
| let _guard = ARG_LOCK.read(); |
| |
| let args_ptr = ARGS.load(Ordering::SeqCst); |
| ensure!( |
| !args_ptr.is_null(), |
| ecust!("Command line arguments are not initialized") |
| ); |
| |
| let os_args = ManuallyDrop::new(Box::from_raw(args_ptr)); |
| Ok(os_args.as_ptr()) |
| } |
| |
| pub unsafe fn initargs(args: Option<Vec<CString>>) -> OCallResult<()> { |
| if !ARGS.load(Ordering::SeqCst).is_null() { |
| return Ok(()); |
| } |
| |
| let args = match args { |
| Some(args) => args, |
| None => { |
| #[cfg(feature = "init_env")] |
| { |
| args_ocall()? |
| } |
| #[cfg(not(feature = "init_env"))] |
| { |
| bail!(eos!(EINVAL)); |
| } |
| } |
| }; |
| |
| let _guard = ARG_LOCK.write(); |
| |
| if ARGS.load(Ordering::SeqCst).is_null() { |
| let os_args = Box::new(OsArgs::new(args).map_err(|e| eos!(e))?); |
| ARGS.store(Box::into_raw(os_args), Ordering::SeqCst); |
| } |
| |
| return Ok(()); |
| |
| #[allow(dead_code)] |
| unsafe fn args_ocall() -> OCallResult<Vec<CString>> { |
| let mut result: ssize_t = 0; |
| let mut error: c_int = 0; |
| let mut buf: Vec<u8> = Vec::with_capacity(4096); |
| |
| loop { |
| let bufsz = buf.capacity(); |
| let status = u_args_ocall( |
| &mut result as *mut ssize_t, |
| &mut error as *mut c_int, |
| buf.as_mut_ptr() as *mut c_uchar, |
| bufsz, |
| ); |
| |
| ensure!(status.is_success(), esgx!(status)); |
| |
| if result >= 0 { |
| let buf_read = result as usize; |
| ensure!(buf_read <= bufsz, ecust!("Malformed return value.")); |
| buf.set_len(buf_read); |
| buf.shrink_to_fit(); |
| break; |
| } else if error != ERANGE { |
| bail!(eos!(error)); |
| } |
| |
| // Trigger the internal buffer resizing logic of `Vec` by requiring |
| // more space than the current capacity. |
| buf.set_len(bufsz); |
| buf.reserve(1); |
| } |
| |
| let args = buf |
| .split(|&c| c == 0) |
| .filter_map(|arg| { |
| if !arg.is_empty() { |
| CString::new(arg).ok() |
| } else { |
| None |
| } |
| }) |
| .collect(); |
| |
| Ok(args) |
| } |
| } |
| |
| #[no_mangle] |
| pub static mut environ: *const *const c_char = ptr::null(); |
| |
| static ENV_LOCK: StaticRwLock = StaticRwLock::new(); |
| static mut ENVIRON: AtomicPtr<OsEnviron> = AtomicPtr::new(ptr::null_mut()); |
| |
| struct OsEnviron<'a> { |
| env: Vec<CString>, |
| ptrs: Vec<*const c_char>, |
| c_environ: &'a mut *const *const c_char, |
| } |
| |
| impl<'a> OsEnviron<'a> { |
| fn new(env: Vec<CString>, c_environ: &'a mut *const *const c_char) -> OsResult<Self> { |
| if !env.is_empty() { |
| ensure!( |
| is_within_enclave(env.as_ptr() as *const u8, env.capacity()), |
| EINVAL |
| ); |
| } |
| |
| let mut ptrs = Vec::with_capacity((env.len() + 1) * mem::size_of::<*const c_char>()); |
| for ptr in env.iter().filter_map(|var| { |
| let bytes = var.as_bytes(); |
| if !bytes.is_empty() && var.as_bytes_with_nul().is_enclave_range() { |
| Some(bytes.as_ptr().cast()) |
| } else { |
| None |
| } |
| }) { |
| ptrs.push(ptr); |
| } |
| ptrs.push(ptr::null()); |
| *c_environ = ptrs.as_ptr(); |
| |
| Ok(Self { |
| env, |
| ptrs, |
| c_environ, |
| }) |
| } |
| |
| #[inline] |
| fn env(&self) -> Vec<CString> { |
| self.env.clone() |
| } |
| |
| fn get(&self, key: &CStr) -> OsResult<Option<CString>> { |
| ensure!(check(key.to_bytes()), EINVAL); |
| |
| Ok(self.env.iter().find_map(|var| { |
| parse(var.as_bytes()) |
| .filter(|&(k, _)| k == key.to_bytes()) |
| .map(|(_, v)| CString::new(v).unwrap()) |
| })) |
| } |
| |
| fn get_ptr(&self, key: &CStr) -> OsResult<Option<*const c_char>> { |
| ensure!(check(key.to_bytes()), EINVAL); |
| |
| Ok(self.env.iter().find_map(|var| { |
| parse(var.as_bytes()) |
| .filter(|&(k, _)| k == key.to_bytes()) |
| .map(|(_, v)| v.as_ptr().cast()) |
| })) |
| } |
| |
| fn get_pos(&self, key: &CStr) -> OsResult<Option<usize>> { |
| ensure!(check(key.to_bytes()), EINVAL); |
| |
| Ok(self.env.iter().enumerate().find_map(|(idx, env)| { |
| parse(env.as_bytes()) |
| .filter(|&(k, _)| k == key.to_bytes()) |
| .map(|(_, _)| idx) |
| })) |
| } |
| |
| fn remove(&mut self, key: &CStr) -> OsResult { |
| if let Some(pos) = self.get_pos(key)? { |
| self.ptrs.remove(pos); |
| self.env.remove(pos); |
| } |
| Ok(()) |
| } |
| |
| fn set(&mut self, key: &CStr, value: &CStr, overwrite: bool) -> OsResult { |
| let pos = self.get_pos(key)?; |
| if !overwrite && pos.is_some() { |
| return Ok(()); |
| } |
| |
| let env = { |
| let mut bytes = Vec::with_capacity(key.to_bytes().len() + value.to_bytes().len() + 1); |
| bytes.extend_from_slice(key.to_bytes()); |
| bytes.push(b'='); |
| bytes.extend_from_slice(value.to_bytes()); |
| CString::new(bytes).unwrap() |
| }; |
| let c_ptr = env.as_ptr().cast(); |
| |
| if let Some(pos) = pos { |
| self.env[pos] = env; |
| self.ptrs[pos] = c_ptr; |
| } else { |
| self.env.push(env); |
| self.ptrs.insert(self.ptrs.len() - 1, c_ptr); |
| *self.c_environ = self.ptrs.as_ptr(); |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| impl<'a> Drop for OsEnviron<'a> { |
| fn drop(&mut self) { |
| *self.c_environ = ptr::null(); |
| } |
| } |
| |
| static ARG_LOCK: StaticRwLock = StaticRwLock::new(); |
| static mut ARGS: AtomicPtr<OsArgs> = AtomicPtr::new(ptr::null_mut()); |
| |
| struct OsArgs { |
| args: Vec<CString>, |
| ptrs: Vec<*const c_char>, |
| } |
| |
| impl OsArgs { |
| fn new(args: Vec<CString>) -> OsResult<Self> { |
| if !args.is_empty() { |
| ensure!( |
| args.is_empty() || is_within_enclave(args.as_ptr() as *const u8, args.capacity()), |
| EINVAL |
| ); |
| } |
| |
| let mut ptrs = Vec::with_capacity((args.len() + 1) * mem::size_of::<*const c_char>()); |
| for ptr in args.iter().filter_map(|arg| { |
| let bytes = arg.as_bytes(); |
| if !bytes.is_empty() && arg.as_bytes_with_nul().is_enclave_range() { |
| Some(bytes.as_ptr().cast()) |
| } else { |
| None |
| } |
| }) { |
| ptrs.push(ptr); |
| } |
| ptrs.push(ptr::null()); |
| |
| Ok(Self { args, ptrs }) |
| } |
| |
| #[inline] |
| fn args(&self) -> Vec<CString> { |
| self.args.clone() |
| } |
| |
| #[inline] |
| fn argc(&self) -> usize { |
| self.args.len() |
| } |
| |
| #[inline] |
| fn as_ptr(&self) -> *const *const c_char { |
| self.ptrs.as_ptr() |
| } |
| } |
| |
| fn check(input: &[u8]) -> bool { |
| if input.is_empty() { |
| return false; |
| } |
| !input.iter().any(|&c| c == b'=') |
| } |
| |
| fn parse(input: &[u8]) -> Option<(&[u8], &[u8])> { |
| if input.is_empty() { |
| return None; |
| } |
| let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); |
| pos.map(|p| (&input[..p], &input[p + 1..])) |
| } |