use std::time::SystemTime;
use crate::seccomp::notif::{read_child_mem, write_child_mem, NotifAction};
use crate::sys::structs::SeccompNotif;
use std::os::unix::io::RawFd;
const TIMER_ABSTIME: u64 = 1;
const CLOCK_MONOTONIC: u32 = 1;
const CLOCK_MONOTONIC_RAW: u32 = 4;
const CLOCK_MONOTONIC_COARSE: u32 = 6;
const CLOCK_BOOTTIME: u32 = 7;
pub(crate) fn calculate_time_offset(time_start: SystemTime) -> i64 {
let now = SystemTime::now();
let desired = time_start
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
let actual = now
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
desired - actual
}
pub(crate) fn handle_timer(
notif: &SeccompNotif,
time_offset: i64,
notif_fd: RawFd,
) -> NotifAction {
if time_offset == 0 {
return NotifAction::Continue;
}
let nr = notif.data.nr as i64;
let flags = notif.data.args[1];
if flags & TIMER_ABSTIME == 0 {
return NotifAction::Continue;
}
if nr == libc::SYS_clock_nanosleep as i64 {
let clockid = (notif.data.args[0] & 0xFFFFFFFF) as u32;
if clockid != CLOCK_MONOTONIC
&& clockid != CLOCK_MONOTONIC_RAW
&& clockid != CLOCK_MONOTONIC_COARSE
&& clockid != CLOCK_BOOTTIME
{
return NotifAction::Continue;
}
let ts_addr = notif.data.args[2];
if ts_addr == 0 {
return NotifAction::Continue;
}
adjust_tv_sec(notif_fd, notif.id, notif.pid, ts_addr, time_offset);
} else {
let itimerspec_addr = notif.data.args[2];
if itimerspec_addr == 0 {
return NotifAction::Continue;
}
adjust_tv_sec(notif_fd, notif.id, notif.pid, itimerspec_addr + 16, time_offset);
}
NotifAction::Continue
}
fn adjust_tv_sec(notif_fd: RawFd, notif_id: u64, pid: u32, addr: u64, offset: i64) {
let bytes = match read_child_mem(notif_fd, notif_id, pid, addr, 8) {
Ok(b) if b.len() == 8 => b,
_ => return,
};
let tv_sec = i64::from_ne_bytes(bytes[..8].try_into().unwrap());
let adjusted = tv_sec - offset;
let _ = write_child_mem(notif_fd, notif_id, pid, addr, &adjusted.to_ne_bytes());
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, SystemTime};
#[test]
fn test_calculate_time_offset_past() {
let past = SystemTime::now() - Duration::from_secs(3600);
let offset = calculate_time_offset(past);
assert!(offset < 0, "past time should give negative offset, got {}", offset);
}
#[test]
fn test_calculate_time_offset_future() {
let future = SystemTime::now() + Duration::from_secs(3600);
let offset = calculate_time_offset(future);
assert!(offset > 0, "future time should give positive offset, got {}", offset);
}
#[test]
fn test_calculate_time_offset_now() {
let now = SystemTime::now();
let offset = calculate_time_offset(now);
assert!(offset.abs() <= 2, "offset for 'now' should be near zero, got {}", offset);
}
#[test]
fn test_adjust_arithmetic() {
let offset: i64 = -3600; let shifted_deadline: i64 = 1700000000;
let adjusted = shifted_deadline - offset;
assert_eq!(adjusted, 1700003600);
}
}