syd 3.41.7

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/hash.rs: Utilities for caching
//
// Copyright (c) 2024, 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

// SAFETY: This module has been liberated from unsafe code!
#![forbid(unsafe_code)]

use std::{
    fs::File,
    sync::{Arc, Condvar, Mutex},
};

use ahash::HashMapExt;
use libseccomp::ScmpSyscall;
use nix::{errno::Errno, unistd::Pid};
use serde::{ser::SerializeMap, Serializer};

use crate::{
    confine::{ScmpNotifReq, SydArch, SydMemoryMap},
    elf::ExecutableFile,
    fs::CanonicalPath,
    hash::SydHashMap,
    sigset::SydSigSet,
};

/// Metadata on a blocking syscall invocation
#[derive(Debug)]
pub struct SysInterrupt {
    /// Syd handler thread ID
    pub handler: Pid,
    /// System call request
    pub request: ScmpNotifReq,
    /// True if `SA_RESTART` is ignored
    /// (e.g. due to a socket timeout).
    pub ignore_restart: bool,
}

/// Map of metadata on blocking syscall invocations.
pub type BlockMap = SydHashMap<u64, SysInterrupt>;

/// Map of restarting signals by TGID.
pub type RestartMap = SydHashMap<Pid, SydSigSet>;

/// This is the data type used to handle syscall interrupts.
#[derive(Debug)]
pub struct SysInterruptMap {
    /// Map of blocking syscalls by request id.
    pub sys_block: Arc<(Mutex<BlockMap>, Condvar)>,
    /// Map of restarting signals by TGID.
    /// Used for SA_RESTART tracking.
    pub sig_restart: Arc<Mutex<RestartMap>>,
}

/// Represents an exec(3) check result
#[derive(Debug)]
pub struct ExecResult {
    pub(crate) exe: ExecutableFile,
    pub(crate) file: File,
    pub(crate) ip: u64,
    pub(crate) sp: u64,
    pub(crate) args: [u64; 6],
    pub(crate) ip_mem: Option<[u8; 64]>,
    pub(crate) sp_mem: Option<[u8; 64]>,
    pub(crate) memmap: Option<Vec<SydMemoryMap>>,
}

/// Syscall-agnostic error map.
pub type ErrorMap = SydHashMap<Pid, Option<Errno>>;

/// chdir(2) result map.
pub type ChdirMap<'a> = SydHashMap<Pid, CanonicalPath<'a>>;

/// exec(3) result map.
pub type ExecvMap = SydHashMap<Pid, ExecResult>;

/// Results map for ptrace(2) hooks chdir, execve, sigaction and sigreturn.
#[derive(Debug)]
pub struct SysResultMap<'a> {
    /// syscall-agnostic error map
    pub trace_error: Arc<Mutex<ErrorMap>>,
    /// chdir(2) result map
    pub trace_chdir: Arc<Mutex<ChdirMap<'a>>>,
    /// exec(3) result map
    pub trace_execv: Arc<Mutex<ExecvMap>>,
}

/// Map of TGIDs that have received count signals for handled signals.
pub type SighandleMap = SydHashMap<Pid, u64>;

/// Signal map, used by signal counting for SROP mitigation:
/// If a TGID is not in sig_handle_map at the entry of sigreturn(2),
/// we terminate the process because the sigreturn(2) is artificial.
#[derive(Debug)]
pub struct SignalMap {
    /// Set of TGIDs that have received count signals for handled signals.
    pub sig_handle: Arc<Mutex<SighandleMap>>,
}

impl SysInterrupt {
    pub(crate) fn new(
        request: ScmpNotifReq,
        handler: Pid,
        ignore_restart: bool,
    ) -> Result<Self, Errno> {
        Ok(Self {
            handler,
            request,
            ignore_restart,
        })
    }
}

impl serde::Serialize for SysInterrupt {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(3))?;

        let data = &self.request.data;
        let syscall = ScmpSyscall::get_name_by_arch(data.syscall, data.arch)
            .unwrap_or_else(|_| format!("{}", i32::from(data.syscall)));
        let _ = map.serialize_entry("pid", &self.request.pid);
        let _ = map.serialize_entry("sys", &syscall);
        let _ = map.serialize_entry("arch", &SydArch(data.arch));
        let _ = map.serialize_entry("args", &data.args);
        let _ = map.serialize_entry("handler", &self.handler.as_raw());
        let _ = map.serialize_entry("ignore_restart", &self.ignore_restart);

        map.end()
    }
}

/// Create a new SysInterruptMap.
pub fn sys_interrupt_map_new() -> SysInterruptMap {
    SysInterruptMap {
        sys_block: Arc::new((Mutex::new(BlockMap::new()), Condvar::new())),
        sig_restart: Arc::new(Mutex::new(RestartMap::new())),
    }
}

/// Create a new SysResultMap.
pub fn sys_result_map_new<'a>() -> SysResultMap<'a> {
    SysResultMap {
        trace_error: Arc::new(Mutex::new(ErrorMap::new())),
        trace_chdir: Arc::new(Mutex::new(ChdirMap::new())),
        trace_execv: Arc::new(Mutex::new(ExecvMap::new())),
    }
}

/// Create a new SignalMap.
pub fn signal_map_new() -> SignalMap {
    SignalMap {
        sig_handle: Arc::new(Mutex::new(SighandleMap::new())),
    }
}