Skip to main content

tpom/
lib.rs

1//! # TPOM
2//! Allows replacing time-related functions in the vDSO ([1](https://man7.org/linux/man-pages/man7/vdso.7.html), [2](https://en.wikipedia.org/wiki/VDSO)) with user-provided functions.  
3//!
4//! Only works on Linux. Is currently limited to x86_64, though it could be extended for other architectures.
5//!
6//! Replaces these functions, if provided:
7//!
8//! |User Function| vDSO|
9//! |-------------|-----|
10//! |ClockGetTime|[clock_gettime](https://linux.die.net/man/3/clock_gettime)|
11//! |ClockGetTimeOfDay|[gettimeofday](https://linux.die.net/man/2/gettimeofday)|
12//! |ClockGetRes|[clock_getres](https://man7.org/linux/man-pages/man2/clock_getres.2.html)|
13//! |ClockGetTime|[time](https://linux.die.net/man/2/time)|
14//!
15//! # Examples
16//! ```
17//! use tpom::*;
18//! use std::time::SystemTime;
19//!
20//! ClockController::overwrite(
21//!     Some(|_| TimeSpec {
22//!         seconds: 1,
23//!         nanos: 1,
24//!     }),
25//!     None,
26//!     None,
27//!     None,
28//! );
29//! // Clock is frozen; all calls to time return the same values
30//! let time_a = SystemTime::now();
31//! let time_b = SystemTime::now();
32//! assert_eq!(time_a, time_b);
33//!
34//! // Restore clock; all calls to time return unique values
35//! ClockController::restore();
36//! let time_c = SystemTime::now();
37//! let time_d = SystemTime::now();
38//! assert_ne!(time_c, time_d);
39//! ```
40
41pub(crate) mod trampolines;
42pub(crate) mod vdso;
43
44use libc;
45use std::collections::HashMap;
46
47use crate::trampolines::*;
48use crate::vdso::vDSO;
49
50#[derive(Debug, Clone, Copy)]
51struct Range {
52    start: usize,
53    end: usize,
54    writable: bool,
55}
56
57pub(crate) type Time = libc::time_t; // as libc::time_t
58
59/// Return type for `ClockGetTime` and `ClockGetRes`; maps to
60/// [libc::timespec](https://docs.rs/libc/0.2.56/libc/struct.timespec.html).
61pub struct TimeSpec {
62    pub seconds: Time,
63    pub nanos: i64, // as libc::c_long
64}
65
66/// Return type for `ClockGetTimeOfDay`; maps to
67/// [libc::timeval](https://docs.rs/libc/0.2.56/libc/struct.timeval.html).
68pub struct TimeVal {
69    pub seconds: Time,
70    pub micros: i64, // as libc::suseconds_t
71}
72
73pub type TimeCb = fn() -> Time;
74
75/// Considered infallible
76pub type ClockGetTimeCb = fn(clockid: i32) -> TimeSpec;
77
78/// Considered infallible
79pub type ClockGetResCb = fn(i32) -> TimeSpec;
80
81/// Considered infallible
82pub type ClockGetTimeOfDayCb = fn() -> TimeVal; // FIXME: Needs to take a TZ
83
84pub struct ClockController {}
85
86impl ClockController {
87    pub fn is_overwritten() -> bool {
88        //! Whether the vDSO is currently overwritten
89        let r = vDSO::find().unwrap();
90        r.writable
91    }
92    pub fn restore() {
93        //! Restore the vDSO to its original state, if it is currently overwritten
94        let r = vDSO::find().unwrap();
95        if !r.writable {
96            return;
97        }
98        if let Ok(b) = BACKUP_VDSO.lock() {
99            if b.len() == 0 {
100                return;
101            }
102            unsafe {
103                std::ptr::copy_nonoverlapping(b.as_ptr(), r.start as *mut u8, b.len());
104                libc::mprotect(
105                    r.start as *mut libc::c_void,
106                    r.end - r.start,
107                    libc::PROT_EXEC | libc::PROT_READ,
108                );
109            }
110        }
111    }
112
113    pub fn overwrite(
114        clockgettime_cb: Option<ClockGetTimeCb>,
115        time_cb: Option<TimeCb>,
116        clock_getres: Option<ClockGetResCb>,
117        gettimeofday: Option<ClockGetTimeOfDayCb>,
118    ) {
119        //! Overwrite the vDSO with the user-provided functions.
120        let mut mapping: HashMap<&'static str, u64> = HashMap::new();
121        if let Some(g) = clockgettime_cb {
122            let mut w = CLOCK_GT_CB.write().unwrap();
123            *w = Some(g);
124            let addr = my_clockgettime as *const () as u64;
125            mapping.insert("clock_gettime", addr);
126            mapping.insert("__vdso_clock_gettime", addr);
127        }
128        if let Some(g) = time_cb {
129            let mut w = TIME_CB.write().unwrap();
130            *w = Some(g);
131            let addr = my_time as *const () as u64;
132            mapping.insert("time", addr);
133            mapping.insert("__vdso_time", addr);
134        }
135        if let Some(g) = clock_getres {
136            let mut w = CLOCK_RES_CB.write().unwrap();
137            *w = Some(g);
138            let addr = my_clockgetres as *const () as u64;
139            mapping.insert("clock_getres", addr);
140            mapping.insert("__vdso_clock_getres", addr);
141        }
142        if let Some(g) = gettimeofday {
143            let mut w = CLOCK_GTOD_CB.write().unwrap();
144            *w = Some(g);
145            let addr = my_gettimeofday as *const () as u64;
146            mapping.insert("gettimeofday", addr);
147            mapping.insert("__vdso_gettimeofday", addr);
148        }
149
150        let r = vDSO::find().unwrap();
151        unsafe {
152            libc::mprotect(
153                r.start as *mut libc::c_void,
154                r.end - r.start,
155                libc::PROT_EXEC | libc::PROT_WRITE | libc::PROT_READ,
156            );
157        }
158        let b = vDSO::read(&r);
159        BACKUP_VDSO.lock().unwrap().clear();
160        BACKUP_VDSO.lock().unwrap().append(&mut b.clone());
161        ClockController::mess_vdso(b, &r, mapping);
162    }
163    fn mess_vdso(buf: Vec<u8>, range: &Range, mapping: HashMap<&'static str, u64>) {
164        for ds in vDSO::dynsyms(buf) {
165            if let Some(dst_addr) = mapping.get(ds.name.as_str()) {
166                // println!("Overriding dyn sym {} at {:x}", sym_name, dst_addr);
167                vDSO::overwrite(range, ds.address, *dst_addr, ds.size as usize);
168            }
169        }
170    }
171}