use {
super::{exception::*, job::Job, job_policy::*, thread::Thread, *},
crate::{object::*, signal::Futex, vm::*},
alloc::{boxed::Box, sync::Arc, vec::Vec},
core::{any::Any, sync::atomic::AtomicI32},
futures::channel::oneshot::{self, Receiver, Sender},
hashbrown::HashMap,
spin::Mutex,
};
#[allow(dead_code)]
pub struct Process {
base: KObjectBase,
_counter: CountHelper,
job: Arc<Job>,
policy: JobPolicy,
vmar: Arc<VmAddressRegion>,
ext: Box<dyn Any + Send + Sync>,
exceptionate: Arc<Exceptionate>,
debug_exceptionate: Arc<Exceptionate>,
inner: Mutex<ProcessInner>,
}
impl_kobject!(Process
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
let inner = self.inner.lock();
let thread = inner.threads.iter().find(|o| o.id() == id).ok_or(ZxError::NOT_FOUND)?;
Ok(thread.clone())
}
fn related_koid(&self) -> KoID {
self.job.id()
}
);
define_count_helper!(Process);
#[derive(Default)]
struct ProcessInner {
status: Status,
max_handle_id: u32,
handles: HashMap<HandleValue, (Handle, Vec<Sender<()>>)>,
futexes: HashMap<usize, Arc<Futex>>,
threads: Vec<Arc<Thread>>,
debug_addr: usize,
dyn_break_on_load: usize,
critical_to_job: Option<(Arc<Job>, bool)>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Status {
Init,
Running,
Exited(i64),
}
impl Default for Status {
fn default() -> Self {
Status::Init
}
}
impl Process {
pub fn create(job: &Arc<Job>, name: &str) -> ZxResult<Arc<Self>> {
Self::create_with_ext(job, name, ())
}
pub fn create_with_ext(
job: &Arc<Job>,
name: &str,
ext: impl Any + Send + Sync,
) -> ZxResult<Arc<Self>> {
let proc = Arc::new(Process {
base: KObjectBase::with_name(name),
_counter: CountHelper::new(),
job: job.clone(),
policy: job.policy(),
vmar: VmAddressRegion::new_root(),
ext: Box::new(ext),
exceptionate: Exceptionate::new(ExceptionChannelType::Process),
debug_exceptionate: Exceptionate::new(ExceptionChannelType::Debugger),
inner: Mutex::new(ProcessInner::default()),
});
job.add_process(proc.clone())?;
Ok(proc)
}
pub fn start(
&self,
thread: &Arc<Thread>,
entry: usize,
stack: usize,
arg1: Option<Handle>,
arg2: usize,
thread_fn: ThreadFn,
) -> ZxResult {
let handle_value;
{
let mut inner = self.inner.lock();
if !inner.contains_thread(thread) {
return Err(ZxError::ACCESS_DENIED);
}
if inner.status != Status::Init {
return Err(ZxError::BAD_STATE);
}
inner.status = Status::Running;
handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle));
}
thread.set_first_thread();
match thread.start(entry, stack, handle_value as usize, arg2, thread_fn) {
Ok(_) => Ok(()),
Err(err) => {
let mut inner = self.inner.lock();
if handle_value != INVALID_HANDLE {
inner.remove_handle(handle_value).ok();
}
Err(err)
}
}
}
pub fn exit(&self, retcode: i64) {
let mut inner = self.inner.lock();
if let Status::Exited(_) = inner.status {
return;
}
inner.status = Status::Exited(retcode);
if inner.threads.is_empty() {
inner.handles.clear();
drop(inner);
self.terminate();
return;
}
for thread in inner.threads.iter() {
thread.kill();
}
inner.handles.clear();
}
fn terminate(&self) {
let mut inner = self.inner.lock();
let retcode = match inner.status {
Status::Exited(retcode) => retcode,
_ => {
inner.status = Status::Exited(0);
0
}
};
self.base.signal_set(Signal::PROCESS_TERMINATED);
self.exceptionate.shutdown();
self.debug_exceptionate.shutdown();
self.job.remove_process(self.base.id);
if let Some((job, retcode_nonzero)) = &inner.critical_to_job {
if !retcode_nonzero || retcode != 0 {
job.kill();
}
}
}
pub fn check_policy(&self, condition: PolicyCondition) -> ZxResult {
match self
.policy
.get_action(condition)
.unwrap_or(PolicyAction::Allow)
{
PolicyAction::Allow => Ok(()),
PolicyAction::Deny => Err(ZxError::ACCESS_DENIED),
_ => unimplemented!(),
}
}
pub fn set_critical_at_job(
&self,
critical_to_job: &Arc<Job>,
retcode_nonzero: bool,
) -> ZxResult {
let mut inner = self.inner.lock();
if inner.critical_to_job.is_some() {
return Err(ZxError::ALREADY_BOUND);
}
let mut job = self.job.clone();
loop {
if job.id() == critical_to_job.id() {
inner.critical_to_job = Some((job, retcode_nonzero));
return Ok(());
}
if let Some(p) = job.parent() {
job = p;
} else {
break;
}
}
Err(ZxError::INVALID_ARGS)
}
pub fn status(&self) -> Status {
self.inner.lock().status
}
pub fn ext(&self) -> &Box<dyn Any + Send + Sync> {
&self.ext
}
pub fn vmar(&self) -> Arc<VmAddressRegion> {
self.vmar.clone()
}
pub fn job(&self) -> Arc<Job> {
self.job.clone()
}
pub fn add_handle(&self, handle: Handle) -> HandleValue {
self.inner.lock().add_handle(handle)
}
pub fn add_handles(&self, handles: Vec<Handle>) -> Vec<HandleValue> {
let mut inner = self.inner.lock();
handles.into_iter().map(|h| inner.add_handle(h)).collect()
}
pub fn remove_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
self.inner.lock().remove_handle(handle_value)
}
pub fn remove_handles(&self, handle_values: &[HandleValue]) -> ZxResult<Vec<Handle>> {
let mut inner = self.inner.lock();
handle_values
.iter()
.map(|h| inner.remove_handle(*h))
.collect()
}
pub fn remove_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
let handle = self.remove_handle(handle_value)?;
let object = handle
.object
.downcast_arc::<T>()
.map_err(|_| ZxError::WRONG_TYPE)?;
Ok(object)
}
fn get_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
self.inner.lock().get_handle(handle_value)
}
pub fn get_futex(&self, addr: &'static AtomicI32) -> Arc<Futex> {
let mut inner = self.inner.lock();
inner
.futexes
.entry(addr as *const AtomicI32 as usize)
.or_insert_with(|| Futex::new(addr))
.clone()
}
pub fn dup_handle_operating_rights(
&self,
handle_value: HandleValue,
operation: impl FnOnce(Rights) -> ZxResult<Rights>,
) -> ZxResult<HandleValue> {
let mut inner = self.inner.lock();
let mut handle = match inner.handles.get(&handle_value) {
Some((h, _)) => h.clone(),
None => return Err(ZxError::BAD_HANDLE),
};
handle.rights = operation(handle.rights)?;
let new_handle_value = inner.add_handle(handle);
Ok(new_handle_value)
}
pub fn get_object_with_rights<T: KernelObject>(
&self,
handle_value: HandleValue,
desired_rights: Rights,
) -> ZxResult<Arc<T>> {
self.get_dyn_object_with_rights(handle_value, desired_rights)
.and_then(|obj| obj.downcast_arc::<T>().map_err(|_| ZxError::WRONG_TYPE))
}
pub fn get_object_and_rights<T: KernelObject>(
&self,
handle_value: HandleValue,
) -> ZxResult<(Arc<T>, Rights)> {
let (object, rights) = self.get_dyn_object_and_rights(handle_value)?;
let object = object
.downcast_arc::<T>()
.map_err(|_| ZxError::WRONG_TYPE)?;
Ok((object, rights))
}
pub fn get_dyn_object_with_rights(
&self,
handle_value: HandleValue,
desired_rights: Rights,
) -> ZxResult<Arc<dyn KernelObject>> {
let handle = self.get_handle(handle_value)?;
if !handle.rights.contains(desired_rights) {
return Err(ZxError::ACCESS_DENIED);
}
Ok(handle.object)
}
pub fn get_dyn_object_and_rights(
&self,
handle_value: HandleValue,
) -> ZxResult<(Arc<dyn KernelObject>, Rights)> {
let handle = self.get_handle(handle_value)?;
Ok((handle.object, handle.rights))
}
pub fn get_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
let handle = self.get_handle(handle_value)?;
let object = handle
.object
.downcast_arc::<T>()
.map_err(|_| ZxError::WRONG_TYPE)?;
Ok(object)
}
pub fn get_handle_info(&self, handle_value: HandleValue) -> ZxResult<HandleBasicInfo> {
let handle = self.get_handle(handle_value)?;
Ok(handle.get_info())
}
pub(super) fn add_thread(&self, thread: Arc<Thread>) -> ZxResult {
let mut inner = self.inner.lock();
if let Status::Exited(_) = inner.status {
return Err(ZxError::BAD_STATE);
}
inner.threads.push(thread);
Ok(())
}
pub(super) fn remove_thread(&self, tid: KoID) {
let mut inner = self.inner.lock();
inner.threads.retain(|t| t.id() != tid);
if inner.threads.is_empty() {
drop(inner);
self.terminate();
}
}
pub fn get_info(&self) -> ProcessInfo {
let mut info = ProcessInfo {
debugger_attached: self.debug_exceptionate.has_channel(),
..Default::default()
};
match self.inner.lock().status {
Status::Init => {
info.started = false;
info.has_exited = false;
}
Status::Running => {
info.started = true;
info.has_exited = false;
}
Status::Exited(ret) => {
info.return_code = ret;
info.has_exited = true;
info.started = true;
}
}
info
}
pub fn set_debug_addr(&self, addr: usize) {
self.inner.lock().debug_addr = addr;
}
pub fn get_debug_addr(&self) -> usize {
self.inner.lock().debug_addr
}
pub fn set_dyn_break_on_load(&self, addr: usize) {
self.inner.lock().dyn_break_on_load = addr;
}
pub fn get_dyn_break_on_load(&self) -> usize {
self.inner.lock().dyn_break_on_load
}
pub fn get_cancel_token(&self, handle_value: HandleValue) -> ZxResult<Receiver<()>> {
self.inner.lock().get_cancel_token(handle_value)
}
pub fn thread_ids(&self) -> Vec<KoID> {
self.inner.lock().threads.iter().map(|t| t.id()).collect()
}
pub async fn wait_for_exit(self: &Arc<Self>) -> i64 {
let object: Arc<dyn KernelObject> = self.clone();
object.wait_signal(Signal::PROCESS_TERMINATED).await;
if let Status::Exited(code) = self.status() {
code
} else {
unreachable!();
}
}
}
impl Task for Process {
fn kill(&self) {
self.exit(TASK_RETCODE_SYSCALL_KILL);
}
fn suspend(&self) {
let inner = self.inner.lock();
for thread in inner.threads.iter() {
thread.suspend();
}
}
fn resume(&self) {
let inner = self.inner.lock();
for thread in inner.threads.iter() {
thread.resume();
}
}
fn exceptionate(&self) -> Arc<Exceptionate> {
self.exceptionate.clone()
}
fn debug_exceptionate(&self) -> Arc<Exceptionate> {
self.debug_exceptionate.clone()
}
}
impl ProcessInner {
fn add_handle(&mut self, handle: Handle) -> HandleValue {
let key = (self.max_handle_id << 2) | 0x3u32;
info!("add handle: {:#x}, {:?}", key, handle.object);
self.max_handle_id += 1;
self.handles.insert(key, (handle, Vec::new()));
key
}
fn contains_thread(&self, thread: &Arc<Thread>) -> bool {
self.threads.iter().any(|t| Arc::ptr_eq(t, thread))
}
fn remove_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
let (handle, queue) = self
.handles
.remove(&handle_value)
.ok_or(ZxError::BAD_HANDLE)?;
for sender in queue {
let _ = sender.send(());
}
Ok(handle)
}
fn get_cancel_token(&mut self, handle_value: HandleValue) -> ZxResult<Receiver<()>> {
let (_, queue) = self
.handles
.get_mut(&handle_value)
.ok_or(ZxError::BAD_HANDLE)?;
let (sender, receiver) = oneshot::channel();
queue.push(sender);
Ok(receiver)
}
fn get_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
let (handle, _) = self.handles.get(&handle_value).ok_or(ZxError::BAD_HANDLE)?;
Ok(handle.clone())
}
}
#[allow(missing_docs)]
#[repr(C)]
#[derive(Default)]
pub struct ProcessInfo {
pub return_code: i64,
pub started: bool,
pub has_exited: bool,
pub debugger_attached: bool,
pub padding1: [u8; 5],
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
assert_eq!(proc.related_koid(), root_job.id());
assert!(Arc::ptr_eq(&root_job, &proc.job()));
}
#[test]
fn handle() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
let handle_value = proc.add_handle(handle);
let _info = proc.get_handle_info(handle_value).unwrap();
let object: Arc<Process> = proc
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
.expect("failed to get object");
assert!(Arc::ptr_eq(&object, &proc));
let (object, rights) = proc
.get_object_and_rights::<Process>(handle_value)
.expect("failed to get object");
assert!(Arc::ptr_eq(&object, &proc));
assert_eq!(rights, Rights::DEFAULT_PROCESS);
assert_eq!(
proc.get_object_with_rights::<Process>(handle_value, Rights::MANAGE_JOB)
.err(),
Some(ZxError::ACCESS_DENIED)
);
assert_eq!(
proc.get_object_with_rights::<Job>(handle_value, Rights::DEFAULT_PROCESS)
.err(),
Some(ZxError::WRONG_TYPE)
);
proc.remove_handle(handle_value).unwrap();
assert_eq!(
proc.get_object_with_rights::<Process>(handle_value, Rights::DEFAULT_PROCESS)
.err(),
Some(ZxError::BAD_HANDLE)
);
let handle1 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
let handle2 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
let handle_values = proc.add_handles(vec![handle1, handle2]);
let object1: Arc<Process> = proc
.get_object_with_rights(handle_values[0], Rights::DEFAULT_PROCESS)
.expect("failed to get object");
assert!(Arc::ptr_eq(&object1, &proc));
proc.remove_handles(&handle_values).unwrap();
assert_eq!(
proc.get_object_with_rights::<Process>(handle_values[0], Rights::DEFAULT_PROCESS)
.err(),
Some(ZxError::BAD_HANDLE)
);
}
#[test]
fn handle_duplicate() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
assert_eq!(
proc.dup_handle_operating_rights(0, |_| Ok(Rights::empty())),
Err(ZxError::BAD_HANDLE)
);
let rights = Rights::DUPLICATE;
let handle_value = proc.add_handle(Handle::new(proc.clone(), rights));
let new_handle_value = proc
.dup_handle_operating_rights(handle_value, |old_rights| Ok(old_rights))
.unwrap();
assert_eq!(proc.get_handle(new_handle_value).unwrap().rights, rights);
let new_handle_value = proc
.dup_handle_operating_rights(handle_value, |_| Ok(Rights::empty()))
.unwrap();
assert_eq!(
proc.get_handle(new_handle_value).unwrap().rights,
Rights::empty()
);
let handle_value = proc.add_handle(Handle::new(proc.clone(), Rights::empty()));
assert_eq!(
proc.dup_handle_operating_rights(handle_value, |handle_rights| {
if !handle_rights.contains(Rights::DUPLICATE) {
return Err(ZxError::ACCESS_DENIED);
}
Ok(handle_rights)
}),
Err(ZxError::ACCESS_DENIED)
);
}
#[test]
fn get_child() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
assert_eq!(proc.get_child(thread.id()).unwrap().id(), thread.id());
assert_eq!(proc.get_child(proc.id()).err(), Some(ZxError::NOT_FOUND));
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
assert_eq!(proc.thread_ids(), vec![thread.id(), thread1.id()]);
}
#[test]
fn properties() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
proc.set_debug_addr(123);
assert_eq!(proc.get_debug_addr(), 123);
proc.set_dyn_break_on_load(2);
assert_eq!(proc.get_dyn_break_on_load(), 2);
}
#[test]
fn exit() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
let info = proc.get_info();
assert!(!info.has_exited && !info.started && info.return_code == 0);
proc.exit(666);
let info = proc.get_info();
assert!(info.has_exited && info.started && info.return_code == 666);
assert_eq!(thread.state(), ThreadState::Dying);
assert_eq!(
Thread::create(&proc, "thread1").err(),
Some(ZxError::BAD_STATE)
);
}
#[test]
fn contains_thread() {
let root_job = Job::root();
let proc = Process::create(&root_job, "proc").expect("failed to create process");
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
let thread1 = Thread::create(&proc1, "thread1").expect("failed to create thread");
let inner = proc.inner.lock();
assert!(inner.contains_thread(&thread) && !inner.contains_thread(&thread1));
}
#[test]
fn check_policy() {
let root_job = Job::root();
let policy1 = BasicPolicy {
condition: PolicyCondition::BadHandle,
action: PolicyAction::Allow,
};
let policy2 = BasicPolicy {
condition: PolicyCondition::NewChannel,
action: PolicyAction::Deny,
};
assert!(root_job
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
.is_ok());
let proc = Process::create(&root_job, "proc").expect("failed to create process");
assert!(proc.check_policy(PolicyCondition::BadHandle).is_ok());
assert!(proc.check_policy(PolicyCondition::NewProcess).is_ok());
assert_eq!(
proc.check_policy(PolicyCondition::NewChannel).err(),
Some(ZxError::ACCESS_DENIED)
);
let _job = root_job.create_child().unwrap();
assert_eq!(
root_job
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
.err(),
Some(ZxError::BAD_STATE)
);
}
}