use alloc::sync::Arc;
use super::{
access_policy::{
PerfAccessCapabilities, PerfCredentialIds, PerfCredentialSnapshot, perf_task_access_allowed,
},
target::{PerfCpuId, PerfTarget, PerfTargetError, PerfTargetKind, PerfTaskTarget},
};
use crate::task::{Cred, TidNumber, UserTaskRef, current_user_task, get_user_task_by_number};
pub(super) enum ResolvedPerfTarget {
Task {
task: UserTaskRef,
cpu: Option<PerfCpuId>,
},
Cpu(PerfCpuId),
}
pub(crate) enum AuthorizedPerfTarget {
Task {
task: UserTaskRef,
cpu: Option<PerfCpuId>,
},
Cpu(PerfCpuId),
}
impl ResolvedPerfTarget {
pub(super) const fn kind(&self) -> PerfTargetKind {
match self {
Self::Task { .. } => PerfTargetKind::Task,
Self::Cpu(_) => PerfTargetKind::Cpu,
}
}
pub(super) fn resolve(target: PerfTarget, cpu_count: usize) -> crate::StarryResult<Self> {
match target {
PerfTarget::Task { task, cpu } => {
let current = current_user_task();
let task = match task {
PerfTaskTarget::Current => current,
PerfTaskTarget::Tid(tid) => get_user_task_by_number(TidNumber::try_from(tid)?)?,
};
let cpu = cpu
.resolve_optional(cpu_count)
.map_err(target_error_to_ax)?;
Ok(Self::Task { task, cpu })
}
PerfTarget::Cpu(cpu) => Ok(Self::Cpu(
cpu.resolve_required(cpu_count)
.map_err(target_error_to_ax)?,
)),
}
}
pub(super) fn with_authorized<R>(
self,
signal_delivery: bool,
install: impl FnOnce(AuthorizedPerfTarget) -> crate::StarryResult<R>,
) -> crate::StarryResult<R> {
let (task, cpu) = match self {
Self::Task { task, cpu } => (task, cpu),
Self::Cpu(cpu) => return install(AuthorizedPerfTarget::Cpu(cpu)),
};
let caller = current_user_task();
let target_process = Arc::clone(&task.as_thread().proc_data);
let _exec_guard = target_process.exec_lock().lock();
let caller_thread = caller.as_thread();
let target_thread = task.as_thread();
let caller_cred = caller_thread.cred();
let target_cred = target_thread.cred();
let same_thread_group = Arc::ptr_eq(&caller_thread.proc_data, &target_thread.proc_data);
let allowed = perf_task_access_allowed(
credential_snapshot(&caller_cred),
credential_snapshot(&target_cred),
same_thread_group,
target_thread.proc_data.dumpable() == 1,
signal_delivery,
);
if !allowed {
return Err(crate::StarryError::PermissionDenied);
}
install(AuthorizedPerfTarget::Task { task, cpu })
}
}
impl AuthorizedPerfTarget {
pub(crate) fn task(&self) -> Option<&UserTaskRef> {
match self {
Self::Task { task, .. } => Some(task),
Self::Cpu(_) => None,
}
}
}
fn target_error_to_ax(error: PerfTargetError) -> crate::StarryError {
match error {
PerfTargetError::InvalidTuple => crate::StarryError::InvalidInput,
PerfTargetError::NoSuchProcess => crate::StarryError::NoSuchProcess,
}
}
fn credential_snapshot(cred: &Cred) -> PerfCredentialSnapshot {
PerfCredentialSnapshot::new(
PerfCredentialIds::new(
cred.uid, cred.gid, cred.euid, cred.egid, cred.suid, cred.sgid,
),
PerfAccessCapabilities::new(
cred.has_cap_perfmon(),
cred.has_cap_sys_ptrace(),
cred.has_cap_kill(),
),
)
}