//
// Syd: rock-solid application kernel
// src/kernel/sigaction.rs: {,rt_}sigaction(2) handler
//
// Copyright (c) 2023, 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, sys::signal::SaFlags};

use crate::{confine::is_valid_ptr, proc::proc_tgid, req::UNotifyEventRequest};

pub(crate) fn sys_sigaction(request: UNotifyEventRequest) -> ScmpNotifResp {
    syscall_handler!(request, |request: UNotifyEventRequest| {
        // Check if the handler is a restarting one.
        //
        // This allows us to selectively unblock system calls
        // from the interrupt thread.
        let req = request.scmpreq;

        // Ensure signal number is a valid signal.
        // We deliberately include reserved signals here.
        let sig_num: libc::c_int = match req.data.args[0].try_into().or(Err(Errno::EINVAL))? {
            libc::SIGKILL | libc::SIGSTOP => return Err(Errno::EINVAL),
            sig_num if sig_num < 1 || sig_num > libc::SIGRTMAX() => return Err(Errno::EINVAL),
            sig_num => sig_num,
        };

        // We do not hook into sigaction(2) when the first argument is NULL.
        let addr = req.data.args[1];
        assert_ne!(addr, 0);

        // Check pointer against mmap_min_addr.
        if !is_valid_ptr(addr, req.data.arch) {
            return Err(Errno::EFAULT);
        }

        // Read remote SaFlags.
        let sa_flags = request.read_sa_flags(addr)?;

        // Signal handlers are per-process not per-thread.
        let tgid = proc_tgid(request.scmpreq.pid())?;

        let _is_restart = if sa_flags.contains(SaFlags::SA_RESTART) {
            // This may only fail under memory-pressure.
            // Better to be on the safe side and deny the syscall.
            //
            // TODO: Log an alert here.
            request.cache.add_sig_restart(tgid, sig_num)?;
            true
        } else {
            request.cache.del_sig_restart(tgid, sig_num);
            false
        };

        // SAFETY: Continue system call. There's nothing we can do if
        // the system call fails, or if an attacker changes the sa_flags
        // element of `struct sigaction` but we did our best by
        // validating all the things we can.
        Ok(unsafe { request.continue_syscall() })
    })
}
