blob: 590407c18e74130ac8b7e31682928e4214132e66 [file] [log] [blame]
// 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..]))
}