blob: 91d7febe6fb2f22721d88658999c5fd9312c40ab [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_zircon as zx;
use std::cmp;
use std::convert::TryFrom;
use std::ffi::CString;
use std::fmt;
use std::marker::PhantomData;
use std::sync::Arc;
use crate::auth::*;
use crate::execution::*;
use crate::fs::*;
use crate::loader::*;
use crate::lock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::logging::{not_implemented, set_zx_name};
use crate::mm::MemoryManager;
use crate::signals::signal_handling::dequeue_signal;
use crate::signals::types::*;
use crate::task::*;
use crate::types::*;
pub struct CurrentTask {
task: Arc<Task>,
/// A copy of the registers associated with the Zircon thread. Up-to-date values can be read
/// from `self.handle.read_state_general_regs()`. To write these values back to the thread, call
/// `self.handle.write_state_general_regs(self.registers.into())`.
pub registers: RegisterState,
/// The address of the DT_DEBUG entry in the process' ELF file.
///
/// The value of the DT_DEBUG entry is a pointer to the `r_debug` symbol in the dynamic linker.
/// This struct contains a link map, which the debug agent can use to determine which shared
/// objects have been loaded.
///
/// The lifecycle of this value is as follows (assuming the tag is present):
/// 1. Starnix finds the address of the DT_DEBUG entry and sets `debug_address`.
/// 2. The dynamic linker sets the value of the DT_DEBUG entry to the address of its `r_debug`
/// symbol.
/// 3. Starnix reads the address from the DT_DEBUG entry, writes the address to the process'
/// debug address property, and sets `debug_address` to `None`.
pub dt_debug_address: Option<UserAddress>,
/// Exists only to prevent Sync from being implemented, since CurrentTask should only be used
/// on the thread that runs the task. impl !Sync is a compiler error, the message is confusing
/// but I think it might be nightly only.
_not_sync: std::marker::PhantomData<std::cell::UnsafeCell<()>>,
}
impl std::ops::Drop for CurrentTask {
fn drop(&mut self) {
self.task.destroy();
}
}
impl std::ops::Deref for CurrentTask {
type Target = Task;
fn deref(&self) -> &Self::Target {
&self.task
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ExitStatus {
Exit(u8),
Kill(SignalInfo),
CoreDump(SignalInfo),
Stop(SignalInfo),
Continue(SignalInfo),
}
impl ExitStatus {
/// Converts the given exit status to a status code suitable for returning from wait syscalls.
pub fn wait_status(&self) -> i32 {
match self {
ExitStatus::Exit(status) => (*status as i32) << 8,
ExitStatus::Kill(siginfo) => siginfo.signal.number() as i32,
ExitStatus::CoreDump(siginfo) => (siginfo.signal.number() as i32) | 0x80,
ExitStatus::Continue(_) => 0xffff,
ExitStatus::Stop(siginfo) => 0x7f + ((siginfo.signal.number() as i32) << 8),
}
}
}
pub struct TaskMutableState {
/// The command of this task.
pub command: CString,
/// The arguments with which this task was started.
pub argv: Vec<CString>,
/// The security credentials for this task.
pub creds: Credentials,
// See https://man7.org/linux/man-pages/man2/set_tid_address.2.html
pub clear_child_tid: UserRef<pid_t>,
/// Signal handler related state. This is grouped together for when atomicity is needed during
/// signal sending and delivery.
pub signals: SignalState,
/// The exit status that this task exited with.
pub exit_status: Option<ExitStatus>,
}
pub struct Task {
pub id: pid_t,
/// The thread group to which this task belongs.
pub thread_group: Arc<ThreadGroup>,
/// A handle to the underlying Zircon thread object.
pub thread: zx::Thread,
/// The file descriptor table for this task.
pub files: Arc<FdTable>,
/// The memory manager for this task.
pub mm: Arc<MemoryManager>,
/// The file system for this task.
pub fs: Arc<FsContext>,
/// The namespace for abstract AF_UNIX sockets for this task.
pub abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
/// The namespace for AF_VSOCK for this task.
pub abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
/// The signal this task generates on exit.
pub exit_signal: Option<Signal>,
/// The mutable state of the Task.
mutable_state: RwLock<TaskMutableState>,
}
impl Task {
/// Internal function for creating a Task object. Useful when you need to specify the value of
/// every field. create_process and create_thread are more likely to be what you want.
///
/// Any fields that should be initialized fresh for every task, even if the task was created
/// with fork, are initialized to their defaults inside this function. All other fields are
/// passed as parameters.
fn new(
id: pid_t,
comm: CString,
argv: Vec<CString>,
thread_group: Arc<ThreadGroup>,
thread: zx::Thread,
files: Arc<FdTable>,
mm: Arc<MemoryManager>,
fs: Arc<FsContext>,
creds: Credentials,
abstract_socket_namespace: Arc<AbstractUnixSocketNamespace>,
abstract_vsock_namespace: Arc<AbstractVsockSocketNamespace>,
exit_signal: Option<Signal>,
) -> CurrentTask {
CurrentTask::new(Task {
id,
thread_group,
thread,
files,
mm,
fs,
abstract_socket_namespace,
abstract_vsock_namespace,
exit_signal,
mutable_state: RwLock::new(TaskMutableState {
command: comm,
argv: argv,
creds: creds,
clear_child_tid: UserRef::default(),
signals: Default::default(),
exit_status: None,
}),
})
}
/// Access mutable state with a read lock.
pub fn read(&self) -> RwLockReadGuard<'_, TaskMutableState> {
self.mutable_state.read()
}
/// Access mutable state with a write lock.
pub fn write(&self) -> RwLockWriteGuard<'_, TaskMutableState> {
self.mutable_state.write()
}
/// Create a task that is the leader of a new thread group.
///
/// This function creates an underlying Zircon process to host the new
/// task.
pub fn create_process_without_parent(
kernel: &Arc<Kernel>,
initial_name: CString,
root_fs: Arc<FsContext>,
) -> Result<CurrentTask, Errno> {
let mut pids = kernel.pids.write();
let pid = pids.allocate_pid();
let process_group = ProcessGroup::new(pid, None);
pids.add_process_group(&process_group);
let (thread, thread_group, mm) = create_zircon_process(
kernel,
None,
pid,
process_group.clone(),
SignalActions::default(),
&initial_name,
)?;
{
let thread_group_read_guard = thread_group.read();
process_group.insert(&thread_group_read_guard);
}
let task = Self::new(
pid,
initial_name,
Vec::new(),
thread_group,
thread,
FdTable::new(),
mm,
root_fs,
Credentials::default(),
Arc::clone(&kernel.default_abstract_socket_namespace),
Arc::clone(&kernel.default_abstract_vsock_namespace),
None,
);
task.thread_group.add(&task.task)?;
pids.add_task(&task.task);
pids.add_thread_group(&task.thread_group);
Ok(task)
}
/// Clone this task.
///
/// Creates a new task object that shares some state with this task
/// according to the given flags.
///
/// Used by the clone() syscall to create both processes and threads.
pub fn clone_task(
&self,
flags: u64,
user_parent_tid: UserRef<pid_t>,
user_child_tid: UserRef<pid_t>,
) -> Result<CurrentTask, Errno> {
// TODO: Implement more flags.
const IMPLEMENTED_FLAGS: u64 = (CLONE_VM
| CLONE_FS
| CLONE_FILES
| CLONE_SIGHAND
| CLONE_THREAD
| CLONE_SYSVSEM
| CLONE_SETTLS
| CLONE_PARENT_SETTID
| CLONE_CHILD_CLEARTID
| CLONE_CHILD_SETTID
| CSIGNAL) as u64;
// CLONE_SETTLS is implemented by sys_clone.
let clone_thread = flags & (CLONE_THREAD as u64) != 0;
let clone_vm = flags & (CLONE_VM as u64) != 0;
let clone_sighand = flags & (CLONE_SIGHAND as u64) != 0;
if clone_sighand && !clone_vm {
return error!(EINVAL);
}
if clone_thread && !clone_sighand {
return error!(EINVAL);
}
if clone_thread != clone_vm {
not_implemented!("CLONE_VM without CLONE_THREAD is not implemented");
return error!(ENOSYS);
}
if flags & !IMPLEMENTED_FLAGS != 0 {
not_implemented!("clone does not implement flags: 0x{:x}", flags & !IMPLEMENTED_FLAGS);
return error!(ENOSYS);
}
let raw_child_exist_signal = flags & (CSIGNAL as u64);
let child_exit_signal = if raw_child_exist_signal == 0 {
None
} else {
Some(Signal::try_from(UncheckedSignal::new(raw_child_exist_signal))?)
};
let fs = if flags & (CLONE_FS as u64) != 0 { self.fs.clone() } else { self.fs.fork() };
let files =
if flags & (CLONE_FILES as u64) != 0 { self.files.clone() } else { self.files.fork() };
let kernel = &self.thread_group.kernel;
let mut pids = kernel.pids.write();
let thread_group_state = self.thread_group.write();
let state = self.read();
let pid = pids.allocate_pid();
let comm = state.command.clone();
let argv = state.argv.clone();
let creds = state.creds.clone();
let (thread, thread_group, mm) = if clone_thread {
// Drop both locks when leaving this block
let (state, _thread_group_state) = (state, thread_group_state);
create_zircon_thread(self, &state)?
} else {
// Drop the lock on this task before entering `create_zircon_process`, because it will
// take a lock on the new thread group, and locks on thread groups have a higher
// priority than locks on the task in the thread group.
std::mem::drop(state);
let signal_actions = if clone_sighand {
self.thread_group.signal_actions.clone()
} else {
self.thread_group.signal_actions.fork()
};
let process_group = thread_group_state.process_group.clone();
create_zircon_process(
kernel,
Some(thread_group_state),
pid,
process_group,
signal_actions,
&comm,
)?
};
let child = Self::new(
pid,
comm,
argv,
thread_group,
thread,
files,
mm,
fs,
creds,
self.abstract_socket_namespace.clone(),
self.abstract_vsock_namespace.clone(),
child_exit_signal,
);
// Child lock must be taken before this lock. Drop the lock on the task, take a writable
// lock on the child and take the current state back.
#[cfg(any(test, debug_assertions))]
{
// Take the lock on the thread group and its child in the correct order to ensure any wrong ordering
// will trigger the tracing-mutex at the right call site.
if !clone_thread {
let _l1 = child.thread_group.read();
let _l2 = self.thread_group.read();
}
}
pids.add_task(&child.task);
if clone_thread {
self.thread_group.add(&child.task)?;
} else {
child.thread_group.add(&child.task)?;
let mut child_state = child.write();
let state = self.read();
pids.add_thread_group(&child.thread_group);
child_state.signals.alt_stack = state.signals.alt_stack;
child_state.signals.mask = state.signals.mask;
self.mm.snapshot_to(&child.mm)?;
copy_process_debug_addr(&self.thread_group.process, &child.thread_group.process)?;
}
if flags & (CLONE_PARENT_SETTID as u64) != 0 {
self.mm.write_object(user_parent_tid, &child.id)?;
}
if flags & (CLONE_CHILD_CLEARTID as u64) != 0 {
child.write().clear_child_tid = user_child_tid;
}
if flags & (CLONE_CHILD_SETTID as u64) != 0 {
child.mm.write_object(user_child_tid, &child.id)?;
}
Ok(child)
}
#[cfg(test)]
pub fn clone_task_for_test(&self, flags: u64) -> CurrentTask {
let result = self
.clone_task(flags, UserRef::default(), UserRef::default())
.expect("failed to create task in test");
// Take the lock on thread group and task in the correct order to ensure any wrong ordering
// will trigger the tracing-mutex at the right call site.
{
let _l1 = result.thread_group.read();
let _l2 = result.read();
}
result
}
/// If needed, clear the child tid for this task.
///
/// Userspace can ask us to clear the child tid and issue a futex wake at
/// the child tid address when we tear down a task. For example, bionic
/// uses this mechanism to implement pthread_join. The thread that calls
/// pthread_join sleeps using FUTEX_WAIT on the child tid address. We wake
/// them up here to let them know the thread is done.
fn clear_child_tid_if_needed(&self) -> Result<(), Errno> {
let mut state = self.write();
let user_tid = state.clear_child_tid;
if !user_tid.is_null() {
let zero: pid_t = 0;
self.mm.write_object(user_tid, &zero)?;
self.mm.futex.wake(user_tid.addr(), usize::MAX, FUTEX_BITSET_MATCH_ANY);
state.clear_child_tid = UserRef::default();
}
Ok(())
}
/// Called by the Drop trait on CurrentTask.
fn destroy(self: &Arc<Self>) {
let _ignored = self.clear_child_tid_if_needed();
self.thread_group.remove(self);
}
pub fn get_task(&self, pid: pid_t) -> Option<Arc<Task>> {
self.thread_group.kernel.pids.read().get_task(pid)
}
pub fn get_pid(&self) -> pid_t {
self.thread_group.leader
}
pub fn get_tid(&self) -> pid_t {
self.id
}
pub fn as_ucred(&self) -> ucred {
let state = self.read();
ucred { pid: self.get_pid(), uid: state.creds.uid, gid: state.creds.gid }
}
pub fn as_fscred(&self) -> FsCred {
let state = self.read();
FsCred { uid: state.creds.euid, gid: state.creds.egid }
}
pub fn can_signal(&self, target: &Task, unchecked_signal: &UncheckedSignal) -> bool {
// If both the tasks share a thread group the signal can be sent. This is not documented
// in kill(2) because kill does not support task-level granularity in signal sending.
if self.thread_group == target.thread_group {
return true;
}
let self_creds = self.read().creds.clone();
if self_creds.has_capability(CAP_KILL) {
return true;
}
if self_creds.has_same_uid(&target.read().creds) {
return true;
}
// TODO(lindkvist): This check should also verify that the sessions are the same.
if Signal::try_from(unchecked_signal) == Ok(SIGCONT) {
return true;
}
false
}
/// Interrupts the current task.
///
/// This will interrupt any blocking syscalls if the task is blocked on one.
/// The signal_state of the task must not be locked.
///
/// Returns whether the task was interrupted.
///
/// TODO(qsr): This should also interrupt any running code.
pub fn interrupt(&self, interruption_type: InterruptionType) -> bool {
if let Some(waiter) = &self.read().signals.waiter {
waiter.interrupt(interruption_type)
} else {
false
}
}
}
impl CurrentTask {
fn new(task: Task) -> CurrentTask {
CurrentTask {
task: Arc::new(task),
dt_debug_address: None,
registers: RegisterState::default(),
_not_sync: PhantomData,
}
}
pub fn kernel(&self) -> &Arc<Kernel> {
&self.thread_group.kernel
}
pub fn task_arc_clone(&self) -> Arc<Task> {
Arc::clone(&self.task)
}
/// Sets the task's signal mask to `signal_mask` and runs `wait_function`.
///
/// Signals are dequeued prior to the original signal mask being restored.
///
/// The returned result is the result returned from the wait function.
pub fn wait_with_temporary_mask<F, T>(
&mut self,
signal_mask: u64,
wait_function: F,
) -> Result<T, Errno>
where
F: FnOnce(&CurrentTask) -> Result<T, Errno>,
{
let old_mask = self.write().signals.set_signal_mask(signal_mask);
let wait_result = wait_function(self);
dequeue_signal(self);
self.write().signals.set_signal_mask(old_mask);
wait_result
}
/// Determine namespace node indicated by the dir_fd.
///
/// Returns the namespace node and the path to use relative to that node.
pub fn resolve_dir_fd<'a>(
&self,
dir_fd: FdNumber,
mut path: &'a FsStr,
) -> Result<(NamespaceNode, &'a FsStr), Errno> {
let dir = if !path.is_empty() && path[0] == b'/' {
path = &path[1..];
self.fs.root()
} else if dir_fd == FdNumber::AT_FDCWD {
self.fs.cwd()
} else {
let file = self.files.get(dir_fd)?;
file.name.clone()
};
Ok((dir, path))
}
/// A convenient wrapper for opening files relative to FdNumber::AT_FDCWD.
///
/// Returns a FileHandle but does not install the FileHandle in the FdTable
/// for this task.
pub fn open_file(&self, path: &FsStr, flags: OpenFlags) -> Result<FileHandle, Errno> {
if flags.contains(OpenFlags::CREAT) {
// In order to support OpenFlags::CREAT we would need to take a
// FileMode argument.
return error!(EINVAL);
}
self.open_file_at(FdNumber::AT_FDCWD, path, flags, FileMode::default())
}
/// Resolves a path for open.
///
/// If the final path component points to a symlink, the symlink is followed (as long as
/// the symlink traversal limit has not been reached).
///
/// If the final path component (after following any symlinks, if enabled) does not exist,
/// and `flags` contains `OpenFlags::CREAT`, a new node is created at the location of the
/// final path component.
fn resolve_open_path(
&self,
context: &mut LookupContext,
dir: NamespaceNode,
path: &FsStr,
mode: FileMode,
flags: OpenFlags,
) -> Result<NamespaceNode, Errno> {
let path = context.update_for_path(&path);
let mut parent_content = context.with(SymlinkMode::Follow);
let (parent, basename) = self.lookup_parent(&mut parent_content, dir.clone(), path)?;
context.remaining_follows = parent_content.remaining_follows;
let must_create = flags.contains(OpenFlags::CREAT) && flags.contains(OpenFlags::EXCL);
let mut child_context = context.with(SymlinkMode::NoFollow);
match parent.lookup_child(self, &mut child_context, basename) {
Ok(name) => {
if name.entry.node.is_lnk() {
if context.symlink_mode == SymlinkMode::NoFollow
|| context.remaining_follows == 0
{
if must_create {
// Since `must_create` is set, and a node was found, this returns EEXIST
// instead of ELOOP.
return error!(EEXIST);
}
// A symlink was found, but too many symlink traversals have been
// attempted.
return error!(ELOOP);
}
context.remaining_follows -= 1;
match name.entry.node.readlink(self)? {
SymlinkTarget::Path(path) => {
let dir = if path[0] == b'/' { self.fs.root() } else { parent };
self.resolve_open_path(context, dir, &path, mode, flags)
}
SymlinkTarget::Node(node) => Ok(node),
}
} else {
if must_create {
return error!(EEXIST);
}
Ok(name)
}
}
Err(e) if e == errno!(ENOENT) && flags.contains(OpenFlags::CREAT) => {
if context.must_be_directory {
return error!(EISDIR);
}
parent.create_node(
self,
&basename,
mode.with_type(FileMode::IFREG),
DeviceType::NONE,
)
}
error => error,
}
}
/// The primary entry point for opening files relative to a task.
///
/// Absolute paths are resolve relative to the root of the FsContext for
/// this task. Relative paths are resolve relative to dir_fd. To resolve
/// relative to the current working directory, pass FdNumber::AT_FDCWD for
/// dir_fd.
///
/// Returns a FileHandle but does not install the FileHandle in the FdTable
/// for this task.
pub fn open_file_at(
&self,
dir_fd: FdNumber,
path: &FsStr,
flags: OpenFlags,
mode: FileMode,
) -> Result<FileHandle, Errno> {
let mut flags = flags;
if flags.contains(OpenFlags::PATH) {
// When O_PATH is specified in flags, flag bits other than O_CLOEXEC,
// O_DIRECTORY, and O_NOFOLLOW are ignored.
const ALLOWED_FLAGS: OpenFlags = OpenFlags::from_bits_truncate(
OpenFlags::PATH.bits()
| OpenFlags::CLOEXEC.bits()
| OpenFlags::DIRECTORY.bits()
| OpenFlags::NOFOLLOW.bits(),
);
flags = flags & ALLOWED_FLAGS;
}
let nofollow = flags.contains(OpenFlags::NOFOLLOW);
let must_create = flags.contains(OpenFlags::CREAT) && flags.contains(OpenFlags::EXCL);
let symlink_mode =
if nofollow || must_create { SymlinkMode::NoFollow } else { SymlinkMode::Follow };
if path.is_empty() {
return error!(ENOENT);
}
let (dir, path) = self.resolve_dir_fd(dir_fd, path)?;
let mut context = LookupContext::new(symlink_mode);
context.must_be_directory = flags.contains(OpenFlags::DIRECTORY);
let name = self.resolve_open_path(&mut context, dir, path, mode, flags)?;
// Be sure not to reference the mode argument after this point.
let mode = name.entry.node.info().mode;
if nofollow && mode.is_lnk() {
return error!(ELOOP);
}
// Mode only applies to future accesses of newly created files.
if !flags.contains(OpenFlags::CREAT) {
name.entry.node.check_access(self, Access::from_open_flags(flags))?;
}
if mode.is_dir() {
if flags.can_write()
|| flags.contains(OpenFlags::CREAT)
|| flags.contains(OpenFlags::TRUNC)
{
return error!(EISDIR);
}
} else if context.must_be_directory {
return error!(ENOTDIR);
}
if flags.contains(OpenFlags::TRUNC) && mode.is_reg() {
// You might think we should check file.can_write() at this
// point, which is what the docs suggest, but apparently we
// are supposed to truncate the file if this task can write
// to the underlying node, even if we are opening the file
// as read-only. See OpenTest.CanTruncateReadOnly.
// TODO(security): We should really do an access check for whether
// this task can write to this file.
if mode.contains(FileMode::IWUSR) {
name.entry.node.truncate(0)?;
}
}
name.open(self, flags)
}
/// A wrapper for FsContext::lookup_parent_at that resolves the given
/// dir_fd to a NamespaceNode.
///
/// Absolute paths are resolve relative to the root of the FsContext for
/// this task. Relative paths are resolve relative to dir_fd. To resolve
/// relative to the current working directory, pass FdNumber::AT_FDCWD for
/// dir_fd.
pub fn lookup_parent_at<'a>(
&self,
dir_fd: FdNumber,
path: &'a FsStr,
) -> Result<(NamespaceNode, &'a FsStr), Errno> {
let (dir, path) = self.resolve_dir_fd(dir_fd, path)?;
let mut context = LookupContext::default();
self.lookup_parent(&mut context, dir, path)
}
/// Lookup the parent of a namespace node.
///
/// Consider using Task::open_file_at or Task::lookup_parent_at rather than
/// calling this function directly.
///
/// This function resolves all but the last component of the given path.
/// The function returns the parent directory of the last component as well
/// as the last component.
///
/// If path is empty, this function returns dir and an empty path.
/// Similarly, if path ends with "." or "..", these components will be
/// returned along with the parent.
///
/// The returned parent might not be a directory.
fn lookup_parent<'a>(
&self,
context: &mut LookupContext,
dir: NamespaceNode,
path: &'a FsStr,
) -> Result<(NamespaceNode, &'a FsStr), Errno> {
let mut current_node = dir;
let mut it = path.split(|c| *c == b'/');
let mut current_path_component = it.next().unwrap_or(b"");
while let Some(next_path_component) = it.next() {
current_node = current_node.lookup_child(self, context, current_path_component)?;
current_path_component = next_path_component;
}
Ok((current_node, current_path_component))
}
/// Lookup a namespace node.
///
/// Consider using Task::open_file_at or Task::lookup_parent_at rather than
/// calling this function directly.
///
/// This function resolves the component of the given path.
pub fn lookup_path(
&self,
context: &mut LookupContext,
dir: NamespaceNode,
path: &FsStr,
) -> Result<NamespaceNode, Errno> {
let (parent, basename) = self.lookup_parent(context, dir, path)?;
parent.lookup_child(self, context, basename)
}
/// Lookup a namespace node starting at the root directory.
///
/// Resolves symlinks.
pub fn lookup_path_from_root(&self, path: &FsStr) -> Result<NamespaceNode, Errno> {
let mut context = LookupContext::default();
self.lookup_path(&mut context, self.fs.root(), path)
}
pub fn exec(
&mut self,
path: CString,
argv: Vec<CString>,
environ: Vec<CString>,
) -> Result<(), Errno> {
let executable = self.open_file(path.to_bytes(), OpenFlags::RDONLY)?;
let resolved_elf = resolve_executable(self, executable, path.clone(), argv, environ)?;
if let Err(err) = self.finish_exec(path, resolved_elf) {
// TODO(tbodt): Replace this panic with a log and force a SIGSEGV.
panic!("{:?} unrecoverable error in exec: {:?}", self, err);
}
Ok(())
}
/// After the memory is unmapped, any failure in exec is unrecoverable and results in the
/// process crashing. This function is for that second half; any error returned from this
/// function will be considered unrecoverable.
fn finish_exec(&mut self, path: CString, resolved_elf: ResolvedElf) -> Result<(), Errno> {
self.write().argv = resolved_elf.argv.clone();
self.mm
.exec(resolved_elf.file.name.clone())
.map_err(|status| from_status_like_fdio!(status))?;
let start_info = load_executable(self, resolved_elf, &path)?;
self.registers = start_info.to_registers().into();
self.dt_debug_address = start_info.dt_debug_address;
self.thread_group.signal_actions.reset_for_exec();
self.write().signals.alt_stack = None;
// TODO: The termination signal is reset to SIGCHLD.
// TODO: All threads other than the calling thread are destroyed.
// TODO: The file descriptor table is unshared, undoing the effect of
// the CLONE_FILES flag of clone(2).
//
// To make this work, we can put the files in an RwLock and then cache
// a reference to the files on the CurrentTask. That will let
// functions that have CurrentTask access the FdTable without
// needing to grab the read-lock.
//
// For now, we do not implement that behavior.
self.files.exec();
// TODO: POSIX timers are not preserved.
self.thread_group.write().did_exec = true;
set_zx_name(&self.thread_group.process, path.as_bytes());
set_zx_name(&fuchsia_runtime::thread_self(), path.as_bytes());
// Get the basename of the path, which will be used as the name displayed with
// `prctl(PR_GET_NAME)` and `/proc/self/stat`
let basename = if let Some(idx) = memchr::memrchr(b'/', path.to_bytes()) {
// SAFETY: Substring of a CString will contain no null bytes.
CString::new(&path.to_bytes()[idx + 1..]).unwrap()
} else {
path
};
self.set_command_name(basename);
Ok(())
}
pub fn set_command_name(&self, name: CString) {
// Truncate to 16 bytes, including null byte.
let bytes = name.to_bytes();
self.write().command = if bytes.len() > 15 {
// SAFETY: Substring of a CString will contain no null bytes.
CString::new(&bytes[..15]).unwrap()
} else {
name
};
}
}
impl fmt::Debug for Task {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[{}]", self.id, self.read().command.to_string_lossy())
}
}
impl fmt::Debug for CurrentTask {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.task.fmt(f)
}
}
impl cmp::PartialEq for Task {
fn eq(&self, other: &Self) -> bool {
let ptr: *const Task = self;
let other_ptr: *const Task = other;
return ptr == other_ptr;
}
}
impl cmp::Eq for Task {}
/// The state of the task's registers when the thread of execution entered the kernel.
/// This is a thin wrapper around [`zx::sys::zx_thread_state_general_regs_t`] that also stores
/// a copy of `rax` in `orig_rax`.
///
/// Implements [`std::ops::Deref`] and [`std::ops::DerefMut`] as a way to get at the underlying
/// [`zx::sys::zx_thread_state_general_regs_t`] that this type wraps.
#[cfg(target_arch = "x86_64")]
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
pub struct RegisterState {
real_registers: zx::sys::zx_thread_state_general_regs_t,
/// A copy of the `rax` register at the time of the `syscall` instruction. This is important to
/// store, as the return value of a syscall overwrites `rax`, making it impossible to recover
/// the original syscall number in the case of syscall restart and strace output.
pub orig_rax: u64,
}
impl std::ops::Deref for RegisterState {
type Target = zx::sys::zx_thread_state_general_regs_t;
fn deref(&self) -> &Self::Target {
&self.real_registers
}
}
impl std::ops::DerefMut for RegisterState {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.real_registers
}
}
impl From<zx::sys::zx_thread_state_general_regs_t> for RegisterState {
fn from(regs: zx::sys::zx_thread_state_general_regs_t) -> Self {
RegisterState { real_registers: regs, orig_rax: 0 }
}
}
impl From<RegisterState> for zx::sys::zx_thread_state_general_regs_t {
fn from(register_state: RegisterState) -> Self {
register_state.real_registers
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::testing::*;
#[::fuchsia::test]
fn test_tid_allocation() {
let (kernel, current_task) = create_kernel_and_task();
assert_eq!(current_task.get_tid(), 1);
let another_current = create_task(&kernel, "another-task");
assert_eq!(another_current.get_tid(), 2);
let pids = kernel.pids.read();
assert_eq!(pids.get_task(1).unwrap().get_tid(), 1);
assert_eq!(pids.get_task(2).unwrap().get_tid(), 2);
assert!(pids.get_task(3).is_none());
}
#[::fuchsia::test]
fn test_clone_pid_and_parent_pid() {
let (_kernel, current_task) = create_kernel_and_task();
let thread =
current_task.clone_task_for_test((CLONE_THREAD | CLONE_VM | CLONE_SIGHAND) as u64);
assert_eq!(current_task.get_pid(), thread.get_pid());
assert_ne!(current_task.get_tid(), thread.get_tid());
assert_eq!(current_task.thread_group.leader, thread.thread_group.leader);
let child_task = current_task.clone_task_for_test(0);
assert_ne!(current_task.get_pid(), child_task.get_pid());
assert_ne!(current_task.get_tid(), child_task.get_tid());
assert_eq!(current_task.get_pid(), child_task.thread_group.read().get_ppid());
}
#[::fuchsia::test]
fn test_root_capabilities() {
let (_kernel, current_task) = create_kernel_and_task();
current_task.write().creds =
Credentials::from_passwd("root:x:0:0").expect("Credentials::from_passwd");
assert!(current_task.read().creds.has_capability(CAP_SYS_ADMIN));
current_task.write().creds =
Credentials::from_passwd("foo:x:1:1").expect("Credentials::from_passwd");
assert!(!current_task.read().creds.has_capability(CAP_SYS_ADMIN));
}
}