syd 3.58.0

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/kernel/timerfd.rs: timerfd_create(2) handler
//
// Copyright (c) 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

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

use libc::{c_int, clockid_t};
use libseccomp::ScmpNotifResp;
use nix::{
    errno::Errno,
    sys::timerfd::{ClockId, TimerFlags},
};

use crate::{
    caps::{util::capable, Capabilities},
    cookie::{safe_exit_group, safe_timerfd_create},
    log_enabled,
    path::XPath,
    req::UNotifyEventRequest,
    sandbox::{Action, Capability, SandboxGuard},
    syslog::LogLevel,
    warn, xfmt,
};

pub(crate) fn sys_timerfd_create(request: UNotifyEventRequest) -> ScmpNotifResp {
    syscall_handler!(request, |request: UNotifyEventRequest| {
        let req = request.scmpreq;

        // Linux rejects unknown flag bits.
        let flags = to_timer_flags(req.data.args[1])?;

        // Linux rejects unsupported clocks.
        let clockid = to_clock_id(req.data.args[0])?;

        // Linux requires CAP_WAKE_ALARM for alarm clocks.
        if matches!(
            clockid,
            ClockId::CLOCK_BOOTTIME_ALARM | ClockId::CLOCK_REALTIME_ALARM
        ) && !capable(Capabilities::CAP_WAKE_ALARM)
        {
            return Err(Errno::EPERM);
        }

        handle_timerfd_create(&request, clockid, flags)
    })
}

fn handle_timerfd_create(
    request: &UNotifyEventRequest,
    clockid: ClockId,
    mut flags: TimerFlags,
) -> Result<ScmpNotifResp, Errno> {
    let sandbox = request.get_sandbox();
    let force_cloexec = sandbox.flags.force_cloexec();
    let force_rand_fd = sandbox.flags.force_rand_fd();
    sandbox_timerfd(request, &sandbox)?;
    drop(sandbox); // release the read lock.

    let cloexec = force_cloexec || flags.contains(TimerFlags::TFD_CLOEXEC);
    flags.insert(TimerFlags::TFD_CLOEXEC);

    let fd = safe_timerfd_create(clockid, flags)?;

    request.send_fd(fd, cloexec, force_rand_fd)
}

#[expect(clippy::cognitive_complexity)]
fn sandbox_timerfd(request: &UNotifyEventRequest, sandbox: &SandboxGuard<'_>) -> Result<(), Errno> {
    let caps = Capability::CAP_CREATE;
    if sandbox.getcaps(caps).is_empty() {
        // Sandboxing is off.
        return Ok(());
    }

    let name = XPath::from_bytes(b"!timerfd");
    let action = sandbox.check_name(caps, name);

    if action.is_logging() && log_enabled!(LogLevel::Warn) {
        if sandbox.log_scmp() {
            warn!("ctx": "access", "cap": caps, "act": action,
                "sys": request.syscall, "path": &name,
                "tip": xfmt!("configure `allow/{caps}+{name}'"),
                "req": request);
        } else {
            warn!("ctx": "access", "cap": caps, "act": action,
                "sys": request.syscall, "path": &name,
                "tip": xfmt!("configure `allow/{caps}+{name}'"),
                "pid": request.scmpreq.pid);
        }
    }

    match action {
        Action::Allow | Action::Warn => Ok(()),
        Action::Deny | Action::Filter => Err(Errno::ENODEV),
        Action::Panic => panic!(),
        Action::Exit => safe_exit_group(Errno::ENODEV as i32),
        action => {
            // Stop|Kill
            let _ = request.kill(action);
            Err(Errno::ENODEV)
        }
    }
}

fn to_clock_id(arg: u64) -> Result<ClockId, Errno> {
    // Linux truncates upper bits.
    #[expect(clippy::cast_possible_truncation)]
    let clockid = arg as clockid_t;

    // Linux rejects unsupported clocks.
    if clockid == ClockId::CLOCK_MONOTONIC as clockid_t {
        Ok(ClockId::CLOCK_MONOTONIC)
    } else if clockid == ClockId::CLOCK_REALTIME as clockid_t {
        Ok(ClockId::CLOCK_REALTIME)
    } else if clockid == ClockId::CLOCK_REALTIME_ALARM as clockid_t {
        Ok(ClockId::CLOCK_REALTIME_ALARM)
    } else if clockid == ClockId::CLOCK_BOOTTIME as clockid_t {
        Ok(ClockId::CLOCK_BOOTTIME)
    } else if clockid == ClockId::CLOCK_BOOTTIME_ALARM as clockid_t {
        Ok(ClockId::CLOCK_BOOTTIME_ALARM)
    } else {
        Err(Errno::EINVAL)
    }
}

fn to_timer_flags(arg: u64) -> Result<TimerFlags, Errno> {
    // Linux truncates upper bits.
    #[expect(clippy::cast_possible_truncation)]
    let flags = arg as c_int;

    // Reject invalid bits.
    TimerFlags::from_bits(flags).ok_or(Errno::EINVAL)
}